Highlight just imported chats in the list
| @@ -7360,8 +7360,9 @@ export function getCurrentChatDetails() { | |||
| 7360 | * The function first fetches the chats, processes them, and then displays them in | 7360 | * The function first fetches the chats, processes them, and then displays them in |
| 7361 | * the HTML. It also has a built-in search functionality that allows filtering the | 7361 | * the HTML. It also has a built-in search functionality that allows filtering the |
| 7362 | * displayed chats based on a search query. | 7362 | * displayed chats based on a search query. |
| 7363 | * @param {string[]} hightlightNames - An array of chat names to highlight | ||
| 7363 | */ | 7364 | */ |
| 7364 | export async function displayPastChats() { | 7365 | export async function displayPastChats(hightlightNames = []) { |
| 7365 | $('#select_chat_div').empty(); | 7366 | $('#select_chat_div').empty(); |
| 7366 | $('#select_chat_search').val('').off('input'); | 7367 | $('#select_chat_search').val('').off('input'); |
| 7367 | 7368 | ||
| @@ -7370,10 +7371,10 @@ export async function displayPastChats() { | |||
| 7370 | const displayName = chatDetails.characterName; | 7371 | const displayName = chatDetails.characterName; |
| 7371 | const avatarImg = chatDetails.avatarImgURL; | 7372 | const avatarImg = chatDetails.avatarImgURL; |
| 7372 | 7373 | ||
| 7373 | await displayChats('', currentChat, displayName, avatarImg, selected_group); | 7374 | await displayChats('', currentChat, displayName, avatarImg, selected_group, hightlightNames); |
| 7374 | 7375 | ||
| 7375 | const debouncedDisplay = debounce((searchQuery) => { | 7376 | const debouncedDisplay = debounce((searchQuery) => { |
| 7376 | displayChats(searchQuery, currentChat, displayName, avatarImg, selected_group); | 7377 | displayChats(searchQuery, currentChat, displayName, avatarImg, selected_group, []); |
| 7377 | }); | 7378 | }); |
| 7378 | 7379 | ||
| 7379 | // Define the search input listener | 7380 | // Define the search input listener |
| @@ -7389,7 +7390,7 @@ export async function displayPastChats() { | |||
| 7389 | }, 200); | 7390 | }, 200); |
| 7390 | } | 7391 | } |
| 7391 | 7392 | ||
| 7392 | async function displayChats(searchQuery, currentChat, displayName, avatarImg, selected_group) { | 7393 | async function displayChats(searchQuery, currentChat, displayName, avatarImg, selected_group, highlightNames) { |
| 7393 | try { | 7394 | try { |
| 7394 | const trimExtension = (fileName) => String(fileName).replace('.jsonl', ''); | 7395 | const trimExtension = (fileName) => String(fileName).replace('.jsonl', ''); |
| 7395 | 7396 | ||
| @@ -7429,6 +7430,12 @@ async function displayChats(searchQuery, currentChat, displayName, avatarImg, se | |||
| 7429 | } | 7430 | } |
| 7430 | 7431 | ||
| 7431 | $('#select_chat_div').append(template); | 7432 | $('#select_chat_div').append(template); |
| 7433 | |||
| 7434 | if (Array.isArray(highlightNames) && highlightNames.includes(chat.file_name)) { | ||
| 7435 | const templateOffset = template.offset().top - template.parent().offset().top; | ||
| 7436 | $('#select_chat_div').scrollTop(templateOffset); | ||
| 7437 | flashHighlight(template, debounce_timeout.extended); | ||
| 7438 | } | ||
| 7432 | } | 7439 | } |
| 7433 | } catch (error) { | 7440 | } catch (error) { |
| 7434 | console.error('Error loading chats:', error); | 7441 | console.error('Error loading chats:', error); |
| @@ -8053,6 +8060,7 @@ export async function saveChatConditional() { | |||
| 8053 | * @param {FormData} formData Form data to send to the server. | 8060 | * @param {FormData} formData Form data to send to the server. |
| 8054 | * @param {object} [options={}] Options for the import | 8061 | * @param {object} [options={}] Options for the import |
| 8055 | * @param {boolean} [options.refresh] Whether to refresh the group chat list after import | 8062 | * @param {boolean} [options.refresh] Whether to refresh the group chat list after import |
| 8063 | * @returns {Promise<string[]>} List of imported file names. | ||
| 8056 | */ | 8064 | */ |
| 8057 | async function importCharacterChat(formData, { refresh = true } = {}) { | 8065 | async function importCharacterChat(formData, { refresh = true } = {}) { |
| 8058 | const fetchResult = await fetch('/api/chats/import', { | 8066 | const fetchResult = await fetch('/api/chats/import', { |
| @@ -8067,7 +8075,10 @@ async function importCharacterChat(formData, { refresh = true } = {}) { | |||
| 8067 | if (data.res && refresh) { | 8075 | if (data.res && refresh) { |
| 8068 | await displayPastChats(); | 8076 | await displayPastChats(); |
| 8069 | } | 8077 | } |
| 8078 | return data?.fileNames || []; | ||
| 8070 | } | 8079 | } |
| 8080 | |||
| 8081 | return []; | ||
| 8071 | } | 8082 | } |
| 8072 | 8083 | ||
| 8073 | function updateViewMessageIds(startFromZero = false) { | 8084 | function updateViewMessageIds(startFromZero = false) { |
| @@ -10616,6 +10627,8 @@ jQuery(async function () { | |||
| 10616 | return; | 10627 | return; |
| 10617 | } | 10628 | } |
| 10618 | 10629 | ||
| 10630 | const importedFileNames = []; | ||
| 10631 | |||
| 10619 | for (const file of targetElement.files) { | 10632 | for (const file of targetElement.files) { |
| 10620 | const ext = file.name.match(/\.(\w+)$/); | 10633 | const ext = file.name.match(/\.(\w+)$/); |
| 10621 | const format = ext?.[1]?.toLowerCase(); | 10634 | const format = ext?.[1]?.toLowerCase(); |
| @@ -10636,10 +10649,15 @@ jQuery(async function () { | |||
| 10636 | formData.set('user_name', name1); | 10649 | formData.set('user_name', name1); |
| 10637 | 10650 | ||
| 10638 | const importFn = selected_group ? importGroupChat : importCharacterChat; | 10651 | const importFn = selected_group ? importGroupChat : importCharacterChat; |
| 10639 | await importFn(formData, { refresh: false }); | 10652 | const result = await importFn(formData, { refresh: false }); |
| 10653 | importedFileNames.push(...result); | ||
| 10654 | } | ||
| 10655 | |||
| 10656 | if (importedFileNames.length > 0) { | ||
| 10657 | toastr.success(t`Successfully imported ${importedFileNames.length} chat(s).`); | ||
| 10640 | } | 10658 | } |
| 10641 | 10659 | ||
| 10642 | await displayPastChats(); | 10660 | await displayPastChats(importedFileNames); |
| 10643 | 10661 | ||
| 10644 | targetElement.value = ''; | 10662 | targetElement.value = ''; |
| 10645 | }); | 10663 | }); |
| @@ -2066,6 +2066,7 @@ export async function deleteGroupChat(groupId, chatId, { jumpToNewChat = true } | |||
| 2066 | * @param {FormData} formData Form data to send to the server | 2066 | * @param {FormData} formData Form data to send to the server |
| 2067 | * @param {object} [options={}] Options for the import | 2067 | * @param {object} [options={}] Options for the import |
| 2068 | * @param {boolean} [options.refresh] Whether to refresh the group chat list after import | 2068 | * @param {boolean} [options.refresh] Whether to refresh the group chat list after import |
| 2069 | * @returns {Promise<string[]>} List of imported file names | ||
| 2069 | */ | 2070 | */ |
| 2070 | export async function importGroupChat(formData, { refresh = true } = {}) { | 2071 | export async function importGroupChat(formData, { refresh = true } = {}) { |
| 2071 | const fetchResult = await fetch('/api/chats/group/import', { | 2072 | const fetchResult = await fetch('/api/chats/group/import', { |
| @@ -2089,7 +2090,11 @@ export async function importGroupChat(formData, { refresh = true } = {}) { | |||
| 2089 | } | 2090 | } |
| 2090 | } | 2091 | } |
| 2091 | } | 2092 | } |
| 2093 | |||
| 2094 | return data?.fileNames || []; | ||
| 2092 | } | 2095 | } |
| 2096 | |||
| 2097 | return []; | ||
| 2093 | } | 2098 | } |
| 2094 | 2099 | ||
| 2095 | export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) { | 2100 | export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) { |
| @@ -628,6 +628,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response) | |||
| 628 | const avatarUrl = (request.body.avatar_url).replace('.png', ''); | 628 | const avatarUrl = (request.body.avatar_url).replace('.png', ''); |
| 629 | const characterName = request.body.character_name; | 629 | const characterName = request.body.character_name; |
| 630 | const userName = request.body.user_name || 'User'; | 630 | const userName = request.body.user_name || 'User'; |
| 631 | const fileNames = []; | ||
| 631 | 632 | ||
| 632 | if (!request.file) { | 633 | if (!request.file) { |
| 633 | return response.sendStatus(400); | 634 | return response.sendStatus(400); |
| @@ -662,6 +663,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response) | |||
| 662 | const handleChat = (chat) => { | 663 | const handleChat = (chat) => { |
| 663 | const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; | 664 | const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; |
| 664 | const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); | 665 | const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); |
| 666 | fileNames.push(fileName); | ||
| 665 | writeFileAtomicSync(filePath, chat, 'utf8'); | 667 | writeFileAtomicSync(filePath, chat, 'utf8'); |
| 666 | }; | 668 | }; |
| 667 | 669 | ||
| @@ -673,7 +675,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response) | |||
| 673 | handleChat(chat); | 675 | handleChat(chat); |
| 674 | } | 676 | } |
| 675 | 677 | ||
| 676 | return response.send({ res: true }); | 678 | return response.send({ res: true, fileNames }); |
| 677 | } | 679 | } |
| 678 | 680 | ||
| 679 | if (format === 'jsonl') { | 681 | if (format === 'jsonl') { |
| @@ -700,13 +702,14 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response) | |||
| 700 | 702 | ||
| 701 | const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; | 703 | const fileName = `${characterName} - ${humanizedISO8601DateTime()} imported.jsonl`; |
| 702 | const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); | 704 | const filePath = path.join(request.user.directories.chats, avatarUrl, fileName); |
| 705 | fileNames.push(fileName); | ||
| 703 | if (flattenedChat !== data) { | 706 | if (flattenedChat !== data) { |
| 704 | writeFileAtomicSync(filePath, flattenedChat, 'utf8'); | 707 | writeFileAtomicSync(filePath, flattenedChat, 'utf8'); |
| 705 | } else { | 708 | } else { |
| 706 | fs.copyFileSync(pathToUpload, filePath); | 709 | fs.copyFileSync(pathToUpload, filePath); |
| 707 | } | 710 | } |
| 708 | fs.unlinkSync(pathToUpload); | 711 | fs.unlinkSync(pathToUpload); |
| 709 | response.send({ res: true }); | 712 | response.send({ res: true, fileNames }); |
| 710 | } | 713 | } |
| 711 | } catch (error) { | 714 | } catch (error) { |
| 712 | console.error(error); | 715 | console.error(error); |