Highlight just imported chats in the list

87d33b4d0befbb4fab22316846956adbb4348679

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

3 files changed, +34 -8Showing whitespace changes
public/script.js+24 -6
@@ -7360,8 +7360,9 @@ export function getCurrentChatDetails() {
7360 * The function first fetches the chats, processes them, and then displays them in7360 * 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 the7361 * 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 */
7364export async function displayPastChats() {7365export 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');
73677368
@@ -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;
73727373
7373 await displayChats('', currentChat, displayName, avatarImg, selected_group);7374 await displayChats('', currentChat, displayName, avatarImg, selected_group, hightlightNames);
73747375
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 });
73787379
7379 // Define the search input listener7380 // Define the search input listener
@@ -7389,7 +7390,7 @@ export async function displayPastChats() {
7389 }, 200);7390 }, 200);
7390}7391}
73917392
7392async function displayChats(searchQuery, currentChat, displayName, avatarImg, selected_group) {7393async 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', '');
73957396
@@ -7429,6 +7430,12 @@ async function displayChats(searchQuery, currentChat, displayName, avatarImg, se
7429 }7430 }
74307431
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 import8061 * @param {object} [options={}] Options for the import
8055 * @param {boolean} [options.refresh] Whether to refresh the group chat list after import8062 * @param {boolean} [options.refresh] Whether to refresh the group chat list after import
8063 * @returns {Promise<string[]>} List of imported file names.
8056 */8064 */
8057async function importCharacterChat(formData, { refresh = true } = {}) {8065async 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}
80728083
8073function updateViewMessageIds(startFromZero = false) {8084function updateViewMessageIds(startFromZero = false) {
@@ -10616,6 +10627,8 @@ jQuery(async function () {
10616 return;10627 return;
10617 }10628 }
1061810629
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);
1063710650
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);
10640 }10654 }
1064110655
10642 await displayPastChats();10656 if (importedFileNames.length > 0) {
10657 toastr.success(t`Successfully imported ${importedFileNames.length} chat(s).`);
10658 }
10659
10660 await displayPastChats(importedFileNames);
1064310661
10644 targetElement.value = '';10662 targetElement.value = '';
10645 });10663 });
public/scripts/group-chats.js+5 -0
@@ -2066,6 +2066,7 @@ export async function deleteGroupChat(groupId, chatId, { jumpToNewChat = true }
2066 * @param {FormData} formData Form data to send to the server2066 * @param {FormData} formData Form data to send to the server
2067 * @param {object} [options={}] Options for the import2067 * @param {object} [options={}] Options for the import
2068 * @param {boolean} [options.refresh] Whether to refresh the group chat list after import2068 * @param {boolean} [options.refresh] Whether to refresh the group chat list after import
2069 * @returns {Promise<string[]>} List of imported file names
2069 */2070 */
2070export async function importGroupChat(formData, { refresh = true } = {}) {2071export 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}
20942099
2095export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {2100export async function saveGroupBookmarkChat(groupId, name, metadata, mesId) {
src/endpoints/chats.js+5 -2
@@ -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 = [];
631632
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 };
667669
@@ -673,7 +675,7 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
673 handleChat(chat);675 handleChat(chat);
674 }676 }
675677
676 return response.send({ res: true });678 return response.send({ res: true, fileNames });
677 }679 }
678680
679 if (format === 'jsonl') {681 if (format === 'jsonl') {
@@ -700,13 +702,14 @@ router.post('/import', validateAvatarUrlMiddleware, function (request, response)
700702
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);