| 8724 | 8753 | * @returns {string} The formatted counter. |
| 8725 | 8754 | */ |
| 8726 | 8755 | function formatSwipeCounter(current, total) { |
| 8727 | 8756 | if (isNaN(current) ||&& isNaN(total)) { |
| 8728 | 8757 | return ''; |
| 8729 | 8758 | } |
| 8730 | | - |
| 8759 | + return `${!isNaN(current) ? current : '?'}\u200b/\u200b${!isNaN(total) ? total : '?'}`; |
| 8731 | | - return `${current}\u200b/\u200b${total}`; |
| 8732 | 8760 | } |
| 8733 | 8761 | |
| 8734 | 8762 | /** |
| 8735 | 8763 | * Handles the swipe to the left event. |
| 8736 | 8764 | * @param {JQuery.Event} _event Event. |
| 8765 | + * @param {'left'|'right'} direction The direction to swipe. |
| 8737 | 8766 | * @param {object} params Additional parameters. |
| 8738 | 8767 | * @param {string} [params.source] The source of the swipe event. |
| 8739 | 8768 | * @param {boolean} [params.repeated] Is the swipe event repeated. |
| 8769 | + * @param {object} [params.message=chat[chat.length - 1]] The chat message to swipe. |
| 8740 | 8770 | */ |
| 8741 | 8771 | export async function swipe_leftswipe(_event, direction, { source, repeated, message = chat[chat.length - 1] } = {}) { |
| 8742 | 8772 | if (chat.length - 1 === Number(this_edit_mes_id)0) { |
| 8743 | | - closeMessageEditor(); |
| 8773 | + console.warn('Swipe was called on an empty chat.'); |
| 8774 | + return; |
| 8744 | 8775 | } |
| 8745 | | - if (isStreamingEnabled() && streamingProcessor) { |
| 8776 | + |
| 8746 | | - streamingProcessor.onStopStreaming(); |
| 8777 | + //Only allow one concurrent swipe. |
| 8778 | + if (!isSwipingAllowed) { |
| 8779 | + console.info('The swipe has been ignored because another is in progress.'); |
| 8780 | + return; |
| 8747 | 8781 | } |
| 8782 | + isSwipingAllowed = false; |
| 8783 | + |
| 8784 | + let generation; |
| 8785 | + let messageIndex; |
| 8748 | 8786 | |
| 8749 | | - // Make sure ad-hoc changes to extras are saved before swiping away |
| 8787 | + //Only set messageIndex if message exists because -1 is truthy. |
| 8750 | | - syncMesToSwipe(); |
| 8788 | + if (message) { |
| 8789 | + messageIndex = chat.indexOf(message); |
| 8790 | + if (messageIndex === -1) { |
| 8791 | + console.error(`The message must exist in chat. ${message};`); |
| 8792 | + return; |
| 8793 | + } |
| 8794 | + } |
| 8751 | 8795 | |
| 8752 | | - // If the user is holding down the key and we're at the first swipe, don't do anything |
| 8796 | + const mesId = Number($(this).closest('.mes').attr('mesid') ?? messageIndex ?? chat.length - 1); |
| 8753 | | - if (source === 'keyboard' && repeated && chat[chat.length - 1].swipe_id === 0) { |
| 8797 | + |
| 8798 | + const thisMesDiv = chatElement.children().filter(`.mes[mesid="${mesId}"]`); |
| 8799 | + const thisMesText = thisMesDiv.find('.mes_block .mes_text'); |
| 8800 | + const thisMesDivHeight = thisMesDiv[0]?.scrollHeight; |
| 8801 | + const thisMesTextHeight = thisMesText[0]?.scrollHeight; |
| 8802 | + if (![thisMesDiv.length, thisMesText.length].every(num => num > 0 )) { |
| 8803 | + console.error(`Message #${mesId}'s DOM element is not valid.`); |
| 8754 | 8804 | return; |
| 8755 | 8805 | } |
| 8806 | + const originalSwipeId = Number(chat[mesId]?.['swipe_id'] ?? 0); |
| 8807 | + let newSwipeId = Number(originalSwipeId); |
| 8756 | 8808 | |
| 8757 | 8809 | const swipe_durationisPristine = 120!chat_metadata?.tainted; |
| 8758 | | - const swipe_range = '700px'; |
| 8810 | + const swipeDuration = Math.round(animation_duration * 1.25); |
| 8759 | | - chat[chat.length - 1]['swipe_id']--; |
| 8811 | + const swipeRange = direction === SWIPE_DIRECTION.RIGHT ? -700 : 700; |
| 8760 | 8812 | |
| 8761 | | - if (chat[chat.length - 1]['swipe_id'] < 0) { |
| 8813 | + async function endSwipe() { |
| 8762 | | - chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length - 1; |
| 8814 | + //Wait for the generation to end. |
| 8815 | + try { |
| 8816 | + await generation; |
| 8817 | + } |
| 8818 | + catch (error) { |
| 8819 | + console.warn(`Swipe failed, Swiping back. ${error}`); |
| 8820 | + } |
| 8821 | + //Allow for another swipe. |
| 8822 | + showSwipeButtons(); |
| 8823 | + |
| 8824 | + //Clamp Id between swipes. |
| 8825 | + let clampedId = clamp(chat[mesId]['swipe_id'], 0, Math.max(0, chat[mesId]['swipes'].length - 1)); |
| 8826 | + |
| 8827 | + //If the id is not within bounds, Swipe back. |
| 8828 | + if (chat[mesId]['swipe_id'] !== clampedId) { |
| 8829 | + chat[mesId]['swipe_id'] = clampedId; |
| 8830 | + syncSwipeToMes(mesId); |
| 8831 | + addOneMessage(chat[mesId], { type: 'swipe', forceId: mesId, scroll: false }); |
| 8832 | + } |
| 8833 | + |
| 8834 | + await updateSwipeCounter(mesId); |
| 8835 | + //Fallback. |
| 8836 | + if (mesId != chat.length - 1) { |
| 8837 | + await updateSwipeCounter(chat.length - 1); |
| 8838 | + } |
| 8763 | 8839 | } |
| 8764 | 8840 | |
| 8765 | | - if (chat[chat.length - 1]['swipe_id'] >= 0) { |
| 8841 | + async function standardSwipe() { |
| 8766 | | - /*$(this).parent().children('swipe_right').css('display', 'flex'); |
| 8842 | + //If swipe_id has changed, or the source is being deleted. |
| 8767 | | - if (chat[chat.length - 1]['swipe_id'] === 0) { |
| 8843 | + if (newSwipeId !== originalSwipeId || source == 'delete') |
| 8768 | | - $(this).css('display', 'none'); |
| 8844 | + { |
| 8769 | | - }*/ // Just in case |
| 8845 | + //Update the chat. |
| 8770 | | - if (!Array.isArray(chat[chat.length - 1]['swipe_info'])) { |
| 8846 | + await loadFromSwipeId(mesId, newSwipeId); |
| 8771 | | - chat[chat.length - 1]['swipe_info'] = []; |
| 8847 | + //Transition to the new chat. |
| 8772 | | - } |
| 8848 | + await animateSwipe(); |
| 8773 | | - let this_mes_div = $(this).parent(); |
| 8849 | + } |
| 8774 | | - let this_mes_block = $(this).parent().children('.mes_block').children('.mes_text'); |
| 8850 | + await endSwipe(); |
| 8775 | | - const this_mes_div_height = this_mes_div[0].scrollHeight; |
| 8851 | + } |
| 8776 | | - this_mes_div.css('height', this_mes_div_height); |
| 8852 | + |
| 8777 | | - const this_mes_block_height = this_mes_block[0].scrollHeight; |
| 8853 | + /** |
| 8778 | | - chat[chat.length - 1]['mes'] = chat[chat.length - 1]['swipes'][chat[chat.length - 1]['swipe_id']]; |
| 8854 | + * Sets the message to the newSwipeId and loads it. |
| 8779 | | - chat[chat.length - 1]['send_date'] = chat[chat.length - 1].swipe_info[chat[chat.length - 1]['swipe_id']]?.send_date || chat[chat.length - 1].send_date; //load the last mes box with the latest generation |
| 8855 | + * @param {number} mesId |
| 8780 | | - chat[chat.length - 1]['extra'] = structuredClone(chat[chat.length - 1].swipe_info[chat[chat.length - 1]['swipe_id']]?.extra || chat[chat.length - 1].extra); |
| 8856 | + * @param {number} newSwipeId |
| 8781 | | - |
| 8857 | + */ |
| 8782 | | - if (chat[chat.length - 1].extra) { |
| 8858 | + async function loadFromSwipeId(mesId, newSwipeId) { |
| 8859 | + //Update the swipe_id. |
| 8860 | + chat[mesId]['swipe_id'] = newSwipeId; |
| 8861 | + |
| 8862 | + if (chat[mesId].extra) { |
| 8783 | 8863 | // if message has memory attached - remove it to allow regen |
| 8784 | 8864 | ifdelete (chat[chat.length - 1mesId].extra.memory) {; |
| 8785 | | - delete chat[chat.length - 1].extra.memory; |
| 8865 | + |
| 8786 | | - } |
| 8787 | 8866 | // ditto for display text |
| 8788 | 8867 | ifdelete (chat[chat.length - 1mesId].extra.display_text) {; |
| 8789 | | - delete chat[chat.length - 1].extra.display_text; |
| 8868 | + |
| 8790 | | - } |
| 8869 | + delete chat[mesId].extra.image; |
| 8791 | | - } |
| 8870 | + delete chat[mesId].extra.image_swipes; |
| 8792 | | - $(this).parent().children('.mes_block').transition({ |
| 8871 | + delete chat[mesId].extra.video; |
| 8793 | | - x: swipe_range, |
| 8872 | + delete chat[mesId].extra.inline_image; |
| 8794 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 8873 | + } |
| 8874 | + delete chat[mesId].gen_started; |
| 8875 | + delete chat[mesId].gen_finished; |
| 8876 | + //load from swipes. |
| 8877 | + syncSwipeToMes(mesId, chat[mesId]['swipe_id']); |
| 8878 | + } |
| 8879 | + |
| 8880 | + //Deepseek-V3.1 |
| 8881 | + // Helper function to convert transition to promise |
| 8882 | + const transitionPromise = (element, properties) => { |
| 8883 | + return new Promise((resolve) => { |
| 8884 | + element.transition({ |
| 8885 | + ...properties, |
| 8886 | + complete: resolve, |
| 8887 | + }); |
| 8888 | + }); |
| 8889 | + }; |
| 8890 | + |
| 8891 | + /** |
| 8892 | + * Animates a swipe for all messages >= mesId. |
| 8893 | + * @param {number} mesId |
| 8894 | + * @param {number} x |
| 8895 | + * @param {number} duration |
| 8896 | + */ |
| 8897 | + async function animateSwipeTransition(mesId, x, duration) { |
| 8898 | + //Selects the swiped message. |
| 8899 | + const swipedMessagesDiv = chatElement.children().filter((index, div) => { |
| 8900 | + const $div = $(div); |
| 8901 | + return mesId === Number($div.attr('mesid')); |
| 8902 | + }); |
| 8903 | + const swipedElementsDiv = swipedMessagesDiv.children('.mes_block, .mesAvatarWrapper'); |
| 8904 | + |
| 8905 | + //Swipe. |
| 8906 | + await transitionPromise(swipedElementsDiv, { |
| 8907 | + x: x, |
| 8908 | + duration: duration, |
| 8795 | 8909 | easing: animation_easing, |
| 8796 | 8910 | queue: false, |
| 8797 | | - complete: async function () { |
| 8911 | + }); |
| 8798 | | - const is_animation_scroll = ($('#chat').scrollTop() >= ($('#chat').prop('scrollHeight') - $('#chat').outerHeight()) - 10); |
| 8912 | + } |
| 8799 | | - //console.log('on left swipe click calling addOneMessage'); |
| 8800 | | - addOneMessage(chat[chat.length - 1], { type: 'swipe' }); |
| 8801 | | - |
| 8802 | | - if (power_user.message_token_count_enabled) { |
| 8803 | | - if (!chat[chat.length - 1].extra) { |
| 8804 | | - chat[chat.length - 1].extra = {}; |
| 8805 | | - } |
| 8806 | 8913 | |
| 8807 | | - const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`); |
| 8914 | + function getMessageBottomHeight(thisMesDiv) { |
| 8808 | | - const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes; |
| 8915 | + const thisMesRect = thisMesDiv[0].getBoundingClientRect(); |
| 8809 | | - const tokenCount = await getTokenCountAsync(tokenCountText, 0); |
| 8916 | + //Scroll position + Chat height = Bottom of chat height. |
| 8810 | | - chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 8917 | + const chatBottom = chatElement.scrollTop() - chatElement.height(); |
| 8811 | | - swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8918 | + //Message offset from viewport top + height = Bottom of message offset. |
| 8812 | | - } |
| 8919 | + const messageBottom = thisMesRect.top + thisMesDiv.height(); |
| 8920 | + // Bottom of chat + Bottom of message offset = target scroll position. |
| 8921 | + const scrollHeight = (chatBottom + messageBottom); |
| 8922 | + return scrollHeight; |
| 8923 | + } |
| 8813 | 8924 | |
| 8814 | | - let new_height = this_mes_div_height - (this_mes_block_height - this_mes_block[0].scrollHeight); |
| 8925 | + function expandNewMessage(thisMesDiv) { |
| 8815 | | - if (new_height < 103) new_height = 103; |
| 8926 | + //Only scroll if the view is not near the bottom. |
| 8816 | | - this_mes_div.animate({ height: new_height + 'px' }, { |
| 8927 | + const is_animation_scroll = (chatElement.scrollTop() >= (chatElement.prop('scrollHeight') - chatElement.outerHeight()) - 10); |
| 8817 | | - duration: 0, //used to be 100 |
| 8818 | | - queue: false, |
| 8819 | | - progress: function () { |
| 8820 | | - // Scroll the chat down as the message expands |
| 8821 | 8928 | |
| 8822 | | - if (is_animation_scroll) $('#chat').scrollTop($('#chat')[0].scrollHeight); |
| 8929 | + let new_height = thisMesDivHeight - (thisMesTextHeight - thisMesText[0].scrollHeight); |
| 8823 | | - }, |
| 8930 | + if (new_height < 103) new_height = 103; |
| 8824 | | - complete: function () { |
| 8825 | | - this_mes_div.css('height', 'auto'); |
| 8826 | | - // Scroll the chat down to the bottom once the animation is complete |
| 8827 | | - if (is_animation_scroll) $('#chat').scrollTop($('#chat')[0].scrollHeight); |
| 8828 | | - }, |
| 8829 | | - }); |
| 8830 | | - $(this).parent().children('.mes_block').transition({ |
| 8831 | | - x: '-' + swipe_range, |
| 8832 | | - duration: 0, |
| 8833 | | - easing: animation_easing, |
| 8834 | | - queue: false, |
| 8835 | | - complete: function () { |
| 8836 | | - $(this).parent().children('.mes_block').transition({ |
| 8837 | | - x: '0px', |
| 8838 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 8839 | | - easing: animation_easing, |
| 8840 | | - queue: false, |
| 8841 | | - complete: async function () { |
| 8842 | | - appendMediaToMessage(chat[chat.length - 1], $(this).parent().children('.mes_block')); |
| 8843 | | - await eventSource.emit(event_types.MESSAGE_SWIPED, (chat.length - 1)); |
| 8844 | | - saveChatDebounced(); |
| 8845 | | - }, |
| 8846 | | - }); |
| 8847 | | - }, |
| 8848 | | - }); |
| 8849 | | - }, |
| 8850 | | - }); |
| 8851 | 8931 | |
| 8852 | | - $(this).parent().children('.avatar').transition({ |
| 8932 | + //Keep the swipe buttons at the same height when scrolling is finished. |
| 8853 | | - x: swipe_range, |
| 8933 | + |
| 8854 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 8934 | + //Expand new message. |
| 8855 | | - easing: animation_easing, |
| 8935 | + thisMesDiv.animate({ height: new_height + 'px' }, { |
| 8936 | + duration: 0, //used to be 100 //Disabled on Cohee's request. https://github.com/SillyTavern/SillyTavern/pull/4610/files#r2408731744 |
| 8856 | 8937 | queue: false, |
| 8857 | 8938 | completeprogress: function (animation, progress, remainingMs) { |
| 8858 | | - $(this).parent().children('.avatar').transition({ |
| 8859 | | - x: '-' + swipe_range, |
| 8860 | | - duration: 0, |
| 8861 | | - easing: animation_easing, |
| 8862 | | - queue: false, |
| 8863 | | - complete: function () { |
| 8864 | | - $(this).parent().children('.avatar').transition({ |
| 8865 | | - x: '0px', |
| 8866 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 8867 | | - easing: animation_easing, |
| 8868 | | - queue: false, |
| 8869 | | - complete: function () { |
| 8870 | 8939 | |
| 8871 | | - }, |
| 8940 | + if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv)); |
| 8872 | | - }); |
| 8941 | + }, |
| 8873 | | - }, |
| 8942 | + complete: function () { |
| 8874 | | - }); |
| 8943 | + thisMesDiv.css('height', 'auto'); |
| 8944 | + //Correct height auto offset. |
| 8945 | + if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv)); |
| 8875 | 8946 | }, |
| 8876 | 8947 | }); |
| 8877 | 8948 | } |
| 8878 | | - if (chat[chat.length - 1]['swipe_id'] < 0) { |
| 8949 | + |
| 8879 | | - chat[chat.length - 1]['swipe_id'] = 0; |
| 8950 | + /** |
| 8951 | + * Anime a swipe, optionally running a generation. |
| 8952 | + * @param {boolean} run_generate |
| 8953 | + */ |
| 8954 | + async function animateSwipe(run_generate = false) { |
| 8955 | + |
| 8956 | + //Swipe out. |
| 8957 | + await animateSwipeTransition(mesId, swipeRange, swipeDuration); |
| 8958 | + |
| 8959 | + if (run_generate) { |
| 8960 | + await updateSwipeCounter(mesId); |
| 8961 | + //shows "..." while generating |
| 8962 | + thisMesDiv.find('.mes_text').html('...'); |
| 8963 | + // resets the timer |
| 8964 | + thisMesDiv.find('.mes_timer').html(''); |
| 8965 | + thisMesDiv.find('.tokenCounterDisplay').text(''); |
| 8966 | + updateReasoningUI(thisMesDiv, { reset: true }); |
| 8967 | + } else { |
| 8968 | + //console.log('showing previously generated swipe candidate, or "..."'); |
| 8969 | + //console.log('onclick right swipe calling addOneMessage'); |
| 8970 | + |
| 8971 | + //Only scroll when swiping the last message. |
| 8972 | + const scroll = (mesId == chat.length - 1); |
| 8973 | + addOneMessage(chat[mesId], { type: 'swipe', forceId: mesId, scroll: scroll }); |
| 8974 | + |
| 8975 | + if (power_user.message_token_count_enabled) { |
| 8976 | + if (!chat[mesId].extra) { |
| 8977 | + chat[mesId].extra = {}; |
| 8978 | + } |
| 8979 | + |
| 8980 | + const tokenCountText = (chat[mesId]?.extra?.reasoning || '') + chat[mesId].mes; |
| 8981 | + const tokenCount = await getTokenCountAsync(tokenCountText, 0); |
| 8982 | + chat[mesId]['extra']['token_count'] = tokenCount; |
| 8983 | + thisMesDiv.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 8984 | + } |
| 8985 | + } |
| 8986 | + |
| 8987 | + //Animate expanding to the new message height. |
| 8988 | + thisMesDiv.css('height', thisMesDivHeight); |
| 8989 | + expandNewMessage(thisMesDiv); |
| 8990 | + |
| 8991 | + |
| 8992 | + //Jump to the opposite side. |
| 8993 | + await animateSwipeTransition(mesId, -swipeRange, 0); |
| 8994 | + |
| 8995 | + appendMediaToMessage(chat[mesId], thisMesDiv); |
| 8996 | + |
| 8997 | + await eventSource.emit(event_types.MESSAGE_SWIPED, (mesId)); |
| 8998 | + |
| 8999 | + if (run_generate && !is_send_press) { |
| 9000 | + is_send_press = true; |
| 9001 | + generation = Generate('swipe'); |
| 9002 | + } else if (parseInt(chat[mesId]['swipe_id']) !== chat[mesId]['swipes'].length) { |
| 9003 | + saveChatDebounced(); |
| 9004 | + } |
| 9005 | + |
| 9006 | + //Swipe in. |
| 9007 | + await animateSwipeTransition(mesId, 0, swipeDuration); |
| 8880 | 9008 | } |
| 8881 | | -} |
| 8882 | 9009 | |
| 8883 | | -/** |
| 9010 | + if (mesId === Number(this_edit_mes_id)) { |
| 8884 | | - * Handles the swipe to the right event. |
| 8885 | | - * @param {JQuery.Event} [_event] Event. |
| 8886 | | - * @param {object} params Additional parameters. |
| 8887 | | - * @param {string} [params.source] The source of the swipe event. |
| 8888 | | - * @param {boolean} [params.repeated] Is the swipe event repeated. |
| 8889 | | - */ |
| 8890 | | -//MARK: swipe_right |
| 8891 | | -export function swipe_right(_event = null, { source, repeated } = {}) { |
| 8892 | | - if (chat.length - 1 === Number(this_edit_mes_id)) { |
| 8893 | 9011 | closeMessageEditor(); |
| 8894 | 9012 | } |
| 9013 | + if (isStreamingEnabled() && streamingProcessor) { |
| 9014 | + streamingProcessor.onStopStreaming(); |
| 9015 | + } |
| 8895 | 9016 | |
| 8896 | 9017 | if (isHordeGenerationNotAllowed()) { |
| 8897 | 9018 | return unblockGeneration(); |
| 8898 | 9019 | } |
| 8899 | 9020 | |
| 8900 | | - // Make sure ad-hoc changes to extras are saved before swiping away |
| 9021 | + //If the swipe is not being deleted. |
| 8901 | | - syncMesToSwipe(); |
| 9022 | + if (source != 'delete') { |
| 8902 | 9023 | |
| 8903 | | - const isPristine = !chat_metadata?.tainted; |
| 9024 | + // Make sure ad-hoc changes to extras are saved before swiping away |
| 8904 | | - const swipe_duration = 200; |
| 9025 | + syncMesToSwipe(mesId); |
| 8905 | | - const swipe_range = 700; |
| 9026 | + |
| 8906 | | - //console.log(swipe_range); |
| 9027 | + if (chat[mesId]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array |
| 8907 | | - let run_generate = false; |
| 9028 | + chat[mesId]['swipe_id'] = 0; // set it to id 0 |
| 8908 | | - let run_swipe_right = false; |
| 9029 | + chat[mesId]['swipes'] = []; // empty the array |
| 8909 | | - if (chat[chat.length - 1]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array |
| 9030 | + chat[mesId]['swipe_info'] = []; |
| 8910 | | - chat[chat.length - 1]['swipe_id'] = 0; // set it to id 0 |
| 9031 | + chat[mesId]['swipes'][0] = chat[mesId]['mes']; //assign swipe array with last chat[mesId] from chat |
| 8911 | | - chat[chat.length - 1]['swipes'] = []; // empty the array |
| 9032 | + chat[mesId]['swipe_info'][0] = { |
| 8912 | | - chat[chat.length - 1]['swipe_info'] = []; |
| 9033 | + 'send_date': chat[mesId]['send_date'], |
| 8913 | | - chat[chat.length - 1]['swipes'][0] = chat[chat.length - 1]['mes']; //assign swipe array with last message from chat |
| 9034 | + 'gen_started': chat[mesId]['gen_started'], |
| 8914 | | - chat[chat.length - 1]['swipe_info'][0] = { |
| 9035 | + 'gen_finished': chat[mesId]['gen_finished'], |
| 8915 | 9036 | 'send_dateextra': structuredClone(chat[chat.length - 1mesId]['send_dateextra']), |
| 8916 | | - 'gen_started': chat[chat.length - 1]['gen_started'], |
| 9037 | + }; |
| 8917 | | - 'gen_finished': chat[chat.length - 1]['gen_finished'], |
| 9038 | + } |
| 8918 | | - 'extra': structuredClone(chat[chat.length - 1]['extra']), |
| 9039 | + // If the user is holding down the key and we're at the last or first swipe, don't do anything. |
| 8919 | | - }; |
| 9040 | + let isLastSwipe = (direction === SWIPE_DIRECTION.RIGHT) ? (chat[mesId].swipe_id === Math.max(0, chat[mesId]['swipes'].length - 1)) : chat[mesId].swipe_id === 0; |
| 8920 | | - //assign swipe info array with last message from chat |
| 9041 | + if (source === 'keyboard' && repeated && isLastSwipe) { |
| 9042 | + await endSwipe(); |
| 9043 | + return; |
| 9044 | + } |
| 9045 | + } else if (source == 'delete') { |
| 9046 | + //If the swipe is being deleted. |
| 9047 | + await standardSwipe(); |
| 9048 | + return; |
| 8921 | 9049 | } |
| 8922 | | - // if swipe_right is called on the last alternate greeting in pristine chats, loop back around |
| 9050 | + |
| 8923 | | - if (chat.length === 1 && chat[0]['swipe_id'] !== undefined && chat[0]['swipe_id'] === chat[0]['swipes'].length - 1 && isPristine) { |
| 9051 | + //If swiping left. |
| 8924 | | - chat[0]['swipe_id'] = 0; |
| 9052 | + if (direction === SWIPE_DIRECTION.LEFT) { |
| 8925 | | - } else { |
| 9053 | + newSwipeId--; |
| 8926 | | - // If the user is holding down the key and we're at the last swipe, don't do anything |
| 9054 | + //Loop to last swipe if negative. |
| 8927 | | - if (source === 'keyboard' && repeated && chat[chat.length - 1].swipe_id === chat[chat.length - 1].swipes.length - 1) { |
| 9055 | + if (newSwipeId < 0) { |
| 9056 | + newSwipeId = Math.max(0, chat[mesId]['swipes'].length - 1); |
| 9057 | + } |
| 9058 | + //Limit swipe_id to swipes. |
| 9059 | + if (newSwipeId > chat[mesId]['swipes'].length - 1) { |
| 9060 | + toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to ${chat[mesId]['swipes'].length - 1}.`); |
| 9061 | + chat[mesId]['swipe_id'] = chat[mesId]['swipes'].length - 1; |
| 9062 | + await endSwipe(); |
| 8928 | 9063 | return; |
| 8929 | 9064 | } |
| 9065 | + await standardSwipe(); |
| 9066 | + return; |
| 9067 | + } |
| 9068 | + //If swiping right. |
| 9069 | + else if (direction === SWIPE_DIRECTION.RIGHT) { |
| 8930 | 9070 | // make new slot in array |
| 8931 | | - chat[chat.length - 1]['swipe_id']++; |
| 9071 | + newSwipeId++; |
| 8932 | | - } |
| 8933 | | - if (chat[chat.length - 1].extra) { |
| 8934 | | - // if message has memory attached - remove it to allow regen |
| 8935 | | - if (chat[chat.length - 1].extra.memory) { |
| 8936 | | - delete chat[chat.length - 1].extra.memory; |
| 8937 | | - } |
| 8938 | | - // ditto for display text |
| 8939 | | - if (chat[chat.length - 1].extra.display_text) { |
| 8940 | | - delete chat[chat.length - 1].extra.display_text; |
| 8941 | | - } |
| 8942 | | - |
| 8943 | | - delete chat[chat.length - 1].extra.image; |
| 8944 | | - delete chat[chat.length - 1].extra.image_swipes; |
| 8945 | | - delete chat[chat.length - 1].extra.video; |
| 8946 | | - delete chat[chat.length - 1].extra.inline_image; |
| 8947 | | - } |
| 8948 | | - if (!Array.isArray(chat[chat.length - 1]['swipe_info'])) { |
| 8949 | | - chat[chat.length - 1]['swipe_info'] = []; |
| 8950 | | - } |
| 8951 | | - //if swipe id of last message is the same as the length of the 'swipes' array and not the greeting |
| 8952 | | - if (parseInt(chat[chat.length - 1]['swipe_id']) === chat[chat.length - 1]['swipes'].length && (chat.length !== 1 || !isPristine)) { |
| 8953 | | - delete chat[chat.length - 1].gen_started; |
| 8954 | | - delete chat[chat.length - 1].gen_finished; |
| 8955 | | - run_generate = true; |
| 8956 | | - } else if (parseInt(chat[chat.length - 1]['swipe_id']) < chat[chat.length - 1]['swipes'].length) { //otherwise, if the id is less than the number of swipes |
| 8957 | | - chat[chat.length - 1]['mes'] = chat[chat.length - 1]['swipes'][chat[chat.length - 1]['swipe_id']]; //load the last mes box with the latest generation |
| 8958 | | - chat[chat.length - 1]['send_date'] = chat[chat.length - 1]?.swipe_info[chat[chat.length - 1]['swipe_id']]?.send_date || chat[chat.length - 1]['send_date']; //update send date |
| 8959 | | - chat[chat.length - 1]['extra'] = structuredClone(chat[chat.length - 1].swipe_info[chat[chat.length - 1]['swipe_id']]?.extra || chat[chat.length - 1].extra || []); |
| 8960 | | - run_swipe_right = true; //then prepare to do normal right swipe to show next message |
| 8961 | | - } |
| 8962 | | - |
| 8963 | | - const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`); |
| 8964 | | - let this_div = currentMessage.find('.swipe_right'); |
| 8965 | | - let this_mes_div = this_div.parent().parent(); |
| 8966 | | - |
| 8967 | | - if (chat[chat.length - 1]['swipe_id'] > chat[chat.length - 1]['swipes'].length) { //if we swipe right while generating (the swipe ID is greater than what we are viewing now) |
| 8968 | | - chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length; //show that message slot (will be '...' while generating) |
| 8969 | | - } |
| 8970 | | - if (run_generate) { //hide swipe arrows while generating |
| 8971 | | - this_div.css('display', 'none'); |
| 8972 | | - } |
| 8973 | | - // handles animated transitions when swipe right, specifically height transitions between messages |
| 8974 | | - if (run_generate || run_swipe_right) { |
| 8975 | | - let this_mes_block = this_mes_div.find('.mes_block .mes_text'); |
| 8976 | | - const this_mes_div_height = this_mes_div[0].scrollHeight; |
| 8977 | | - const this_mes_block_height = this_mes_block[0].scrollHeight; |
| 8978 | | - |
| 8979 | | - this_mes_div.children('.swipe_left').css('display', 'flex'); |
| 8980 | | - this_mes_div.children('.mes_block').transition({ // this moves the div back and forth |
| 8981 | | - x: '-' + swipe_range, |
| 8982 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 8983 | | - easing: animation_easing, |
| 8984 | | - queue: false, |
| 8985 | | - complete: async function () { |
| 8986 | | - const is_animation_scroll = (chatElement.scrollTop() >= (chatElement.prop('scrollHeight') - chatElement.outerHeight()) - 10); |
| 8987 | | - //console.log(parseInt(chat[chat.length-1]['swipe_id'])); |
| 8988 | | - //console.log(chat[chat.length-1]['swipes'].length); |
| 8989 | | - const swipeMessage = chatElement.find('[mesid="' + (chat.length - 1) + '"]'); |
| 8990 | | - if (run_generate && parseInt(chat[chat.length - 1]['swipe_id']) === chat[chat.length - 1]['swipes'].length) { |
| 8991 | | - //shows "..." while generating |
| 8992 | | - swipeMessage.find('.mes_text').html('...'); |
| 8993 | | - // resets the timer |
| 8994 | | - swipeMessage.find('.mes_timer').html(''); |
| 8995 | | - swipeMessage.find('.tokenCounterDisplay').text(''); |
| 8996 | | - updateReasoningUI(swipeMessage, { reset: true }); |
| 8997 | | - } else { |
| 8998 | | - //console.log('showing previously generated swipe candidate, or "..."'); |
| 8999 | | - //console.log('onclick right swipe calling addOneMessage'); |
| 9000 | | - addOneMessage(chat[chat.length - 1], { type: 'swipe' }); |
| 9001 | | - |
| 9002 | | - if (power_user.message_token_count_enabled) { |
| 9003 | | - if (!chat[chat.length - 1].extra) { |
| 9004 | | - chat[chat.length - 1].extra = {}; |
| 9005 | | - } |
| 9006 | 9072 | |
| 9007 | | - const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes; |
| 9073 | + //Minimum of zero. |
| 9008 | | - const tokenCount = await getTokenCountAsync(tokenCountText, 0); |
| 9074 | + if (newSwipeId < 0) { |
| 9009 | | - chat[chat.length - 1]['extra']['token_count'] = tokenCount; |
| 9075 | + toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to zero.`); |
| 9010 | | - swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`); |
| 9076 | + chat[mesId]['swipe_id'] = 0; |
| 9011 | | - } |
| 9077 | + await endSwipe(); |
| 9012 | | - } |
| 9078 | + return; |
| 9013 | | - let new_height = this_mes_div_height - (this_mes_block_height - this_mes_block[0].scrollHeight); |
| 9079 | + } |
| 9014 | | - if (new_height < 103) new_height = 103; |
| 9015 | 9080 | |
| 9081 | + //if swipe id of last message is the same as the length of the 'swipes' array and not the greeting. |
| 9082 | + if (newSwipeId >= chat[mesId]['swipes'].length && ((chat.length !== 1 || !isPristine))) { |
| 9083 | + newSwipeId = chat[mesId]['swipes'].length; |
| 9016 | 9084 | |
| 9017 | | - this_mes_div.animate({ height: new_height + 'px' }, { |
| 9085 | + //Update the swipe_id. |
| 9018 | | - duration: 0, //used to be 100 |
| 9086 | + chat[mesId]['swipe_id'] = newSwipeId; |
| 9019 | | - queue: false, |
| 9020 | | - progress: function () { |
| 9021 | | - // Scroll the chat down as the message expands |
| 9022 | | - if (is_animation_scroll) chatElement.scrollTop(chatElement[0].scrollHeight); |
| 9023 | | - }, |
| 9024 | | - complete: function () { |
| 9025 | | - this_mes_div.css('height', 'auto'); |
| 9026 | | - // Scroll the chat down to the bottom once the animation is complete |
| 9027 | | - if (is_animation_scroll) chatElement.scrollTop(chatElement[0].scrollHeight); |
| 9028 | | - }, |
| 9029 | | - }); |
| 9030 | | - this_mes_div.children('.mes_block').transition({ |
| 9031 | | - x: swipe_range, |
| 9032 | | - duration: 0, |
| 9033 | | - easing: animation_easing, |
| 9034 | | - queue: false, |
| 9035 | | - complete: function () { |
| 9036 | | - this_mes_div.children('.mes_block').transition({ |
| 9037 | | - x: '0px', |
| 9038 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 9039 | | - easing: animation_easing, |
| 9040 | | - queue: false, |
| 9041 | | - complete: async function () { |
| 9042 | | - appendMediaToMessage(chat[chat.length - 1], swipeMessage); |
| 9043 | | - await eventSource.emit(event_types.MESSAGE_SWIPED, (chat.length - 1)); |
| 9044 | | - if (run_generate && !is_send_press && parseInt(chat[chat.length - 1]['swipe_id']) === chat[chat.length - 1]['swipes'].length) { |
| 9045 | | - console.debug('caught here 2'); |
| 9046 | | - is_send_press = true; |
| 9047 | | - await Generate('swipe'); |
| 9048 | | - } else { |
| 9049 | | - if (parseInt(chat[chat.length - 1]['swipe_id']) !== chat[chat.length - 1]['swipes'].length) { |
| 9050 | | - saveChatDebounced(); |
| 9051 | | - } |
| 9052 | | - } |
| 9053 | | - }, |
| 9054 | | - }); |
| 9055 | | - }, |
| 9056 | | - }); |
| 9057 | | - }, |
| 9058 | | - }); |
| 9059 | | - this_mes_div.children('.avatar').transition({ // moves avatar along with swipe |
| 9060 | | - x: '-' + swipe_range, |
| 9061 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 9062 | | - easing: animation_easing, |
| 9063 | | - queue: false, |
| 9064 | | - complete: function () { |
| 9065 | | - this_mes_div.children('.avatar').transition({ |
| 9066 | | - x: swipe_range, |
| 9067 | | - duration: 0, |
| 9068 | | - easing: animation_easing, |
| 9069 | | - queue: false, |
| 9070 | | - complete: function () { |
| 9071 | | - this_mes_div.children('.avatar').transition({ |
| 9072 | | - x: '0px', |
| 9073 | | - duration: animation_duration > 0 ? swipe_duration : 0, |
| 9074 | | - easing: animation_easing, |
| 9075 | | - queue: false, |
| 9076 | | - complete: function () { |
| 9077 | 9087 | |
| 9078 | | - }, |
| 9088 | + //Cancel the generation if it's a user message or the first message in a pristine chat. |
| 9079 | | - }); |
| 9089 | + if (chat[mesId].is_user || (mesId === 0 && isPristine)) { |
| 9080 | | - }, |
| 9090 | + //Cancel swipe. |
| 9081 | | - }); |
| 9091 | + chat[mesId]['swipe_id'] = originalSwipeId; |
| 9082 | | - }, |
| 9092 | + await endSwipe(); |
| 9083 | | - }); |
| 9093 | + return; |
| 9094 | + //Generate. |
| 9095 | + } else { |
| 9096 | + await loadFromSwipeId(mesId, newSwipeId); |
| 9097 | + let run_generate = true; |
| 9098 | + await animateSwipe(run_generate); |
| 9099 | + await endSwipe(); |
| 9100 | + return; |
| 9101 | + } |
| 9102 | + } |
| 9103 | + else { |
| 9104 | + // if swipe_right is called on the last alternate greeting in pristine chats, loop back around |
| 9105 | + if (chat.length === 1 && newSwipeId !== undefined && newSwipeId === chat[0]['swipes'].length && isPristine) { |
| 9106 | + newSwipeId = 0; |
| 9107 | + } |
| 9108 | + } |
| 9109 | + await standardSwipe(); |
| 9110 | + return; |
| 9084 | 9111 | } |
| 9085 | 9112 | } |
| 9086 | 9113 | |
| 9087 | 9114 | /** |
| 9115 | + * @deprecated Use `swipe` instead. |
| 9116 | + * Handles the swipe to the left event. |
| 9117 | + * @param {JQuery.Event} _event Event. |
| 9118 | + * @param {object} params Additional parameters. |
| 9119 | + * @param {string} [params.source] The source of the swipe event. |
| 9120 | + * @param {boolean} [params.repeated] Is the swipe event repeated. |
| 9121 | + * @param {object} [params.message] The chat message to swipe. |
| 9122 | + */ |
| 9123 | +export async function swipe_left(_event, { source, repeated, message } = {}) { |
| 9124 | + await swipe.call(this, _event, SWIPE_DIRECTION.LEFT, { source: source, repeated: repeated, message: message }); |
| 9125 | +} |
| 9126 | + |
| 9127 | +/** |
| 9128 | + * @deprecated Use `swipe` instead. |
| 9129 | + * Handles the swipe to the right event. |
| 9130 | + * @param {JQuery.Event} [_event] Event. |
| 9131 | + * @param {object} params Additional parameters. |
| 9132 | + * @param {string} [params.source] The source of the swipe event. |
| 9133 | + * @param {boolean} [params.repeated] Is the swipe event repeated. |
| 9134 | + * @param {object} [params.message] The chat message to swipe. |
| 9135 | + */ |
| 9136 | +//MARK: swipe_right |
| 9137 | +export async function swipe_right(_event = null, { source, repeated, message } = {}) { |
| 9138 | + await swipe.call(this, _event, SWIPE_DIRECTION.RIGHT, { source: source, repeated: repeated, message: message }); |
| 9139 | +} |
| 9140 | + |
| 9141 | +/** |
| 9088 | 9142 | * Imports supported files dropped into the app window. |
| 9089 | 9143 | * @param {File[]} files Array of files to process |
| 9090 | 9144 | * @param {Map<File, string>} [data] Extra data to pass to the import function |