From 235a1372e82bc93405131ae055d162c496b2e382 Mon Sep 17 00:00:00 2001 From: InspectorCaracal <51038201+InspectorCaracal@users.noreply.github.com> Date: Sat, 22 Mar 2025 23:41:29 -0600 Subject: [PATCH 1/4] fix group chid data attr --- public/scripts/slash-commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index e1f8f0870..2fd403af8 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -3087,7 +3087,7 @@ async function unhideMessageCallback(args, value) { * @returns {void} */ function performGroupMemberAction(chid, action) { - const memberSelector = `.group_member[chid="${chid}"]`; + const memberSelector = `.group_member[data-chid="${chid}"]`; // Do not optimize. Paginator gets recreated on every action const paginationSelector = '#rm_group_members_pagination'; const pageSizeSelector = '#rm_group_members_pagination select'; From fef36bfc39b6dc636fe7eb0988a3bd4ec8f2ad72 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Mon, 17 Mar 2025 20:59:08 +0100 Subject: [PATCH 2/4] Fix deleting swipes overwriting reasoning - Well, someone forgot about syncing extras and mes data again.... - Built the oppositive function of `syncMesToSwipe`, so we can now call `syncSwipeToMes` Fixes #3787 --- public/script.js | 68 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/public/script.js b/public/script.js index 8d3784c71..21b2c3e33 100644 --- a/public/script.js +++ b/public/script.js @@ -6261,7 +6261,6 @@ export function syncMesToSwipe(messageId = null) { } const targetMessage = chat[targetMessageId]; - if (!targetMessage) { return false; } @@ -6294,6 +6293,68 @@ export function syncMesToSwipe(messageId = null) { return true; } +/** + * Syncs swipe data back to the message data at the given message ID (or the last message if no ID is given). + * If the swipe ID is not provided, the current swipe ID in the message object is used. + * + * If the swipe data is invalid in some way, this function will exit out without doing anything. + * @param {number?} [messageId=null] - The ID of the message to sync with the swipe data. If no ID is given, the last message is used. + * @param {number?} [swipeId=null] - The ID of the swipe to sync. If no ID is given, the current swipe ID in the message object is used. + * @returns {boolean} Whether the swipe data was successfully synced to the message + */ +export function syncSwipeToMes(messageId = null, swipeId = null) { + if (!chat.length) { + return false; + } + + const targetMessageId = messageId ?? chat.length - 1; + if (targetMessageId >= chat.length || targetMessageId < 0) { + console.warn(`[syncSwipeToMes] Invalid message ID: ${messageId}`); + return false; + } + + const targetMessage = chat[targetMessageId]; + if (!targetMessage) { + return false; + } + + if (swipeId !== null) { + if (isNaN(swipeId) || swipeId < 0) { + console.warn(`[syncSwipeToMes] Invalid swipe ID: ${swipeId}`); + return false; + } + targetMessage.swipe_id = swipeId; + } + + // No swipe data there yet, exit out + if (typeof targetMessage.swipe_id !== 'number') { + return false; + } + // If swipes structure is invalid, exit out + if (!Array.isArray(targetMessage.swipe_info) || !Array.isArray(targetMessage.swipes)) { + return false; + } + + const targetSwipeId = targetMessage.swipe_id; + if (!targetMessage.swipes[targetSwipeId] || !targetMessage.swipe_info[targetSwipeId]) { + console.warn(`[syncSwipeToMes] Invalid swipe ID: ${targetSwipeId}`); + return false; + } + + const targetSwipeInfo = targetMessage.swipe_info[targetSwipeId]; + if (typeof targetSwipeInfo !== 'object') { + return false; + } + + targetMessage.mes = targetMessage.swipes[targetSwipeId]; + targetMessage.send_date = targetSwipeInfo.send_date; + targetMessage.gen_started = targetSwipeInfo.gen_started; + targetMessage.gen_finished = targetSwipeInfo.gen_finished; + targetMessage.extra = structuredClone(targetSwipeInfo.extra); + + return true; +} + /** * Saves the image to the message object. * @param {ParsedImage} img Image object @@ -8293,10 +8354,9 @@ export async function deleteSwipe(swipeId = null) { lastMessage.swipe_info.splice(swipeId, 1); } - // Select the next swip, or the one before if it was the last one + // Select the next swipe, or the one before if it was the last one const newSwipeId = Math.min(swipeId, lastMessage.swipes.length - 1); - lastMessage.swipe_id = newSwipeId; - lastMessage.mes = lastMessage.swipes[newSwipeId]; + syncSwipeToMes(null, newSwipeId); await saveChatConditional(); await reloadCurrentChat(); From 7431b0e8aa8f96794d8635d77bbda3825875d8aa Mon Sep 17 00:00:00 2001 From: wickedcode Date: Thu, 1 May 2025 02:23:19 -0400 Subject: [PATCH 3/4] fix: replace rmSync with unlinkSync to resolve an issue deleting files with non-English characters in their names --- src/endpoints/avatars.js | 2 +- src/endpoints/characters.js | 2 +- src/endpoints/chats.js | 2 +- src/endpoints/themes.js | 2 +- src/endpoints/thumbnails.js | 2 +- src/endpoints/worldinfo.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/endpoints/avatars.js b/src/endpoints/avatars.js index db52ab643..4362a0058 100644 --- a/src/endpoints/avatars.js +++ b/src/endpoints/avatars.js @@ -28,7 +28,7 @@ router.post('/delete', getFileNameValidationFunction('avatar'), function (reques const fileName = path.join(request.user.directories.avatars, sanitize(request.body.avatar)); if (fs.existsSync(fileName)) { - fs.rmSync(fileName); + fs.unlinkSync(fileName); return response.send({ result: 'ok' }); } diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index 1011e9a3d..d5f549a86 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -1150,7 +1150,7 @@ router.post('/delete', validateAvatarUrlMiddleware, async function (request, res return response.sendStatus(400); } - fs.rmSync(avatarPath); + fs.unlinkSync(avatarPath); invalidateThumbnail(request.user.directories, 'avatar', request.body.avatar_url); let dir_name = (request.body.avatar_url.replace('.png', '')); diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index 9c7376838..2762055a2 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -449,7 +449,7 @@ router.post('/delete', validateAvatarUrlMiddleware, function (request, response) return response.sendStatus(400); } - fs.rmSync(filePath); + fs.unlinkSync(filePath); console.info(`Deleted chat file: ${filePath}`); return response.send('ok'); }); diff --git a/src/endpoints/themes.js b/src/endpoints/themes.js index 868a769d0..00a0dcb97 100644 --- a/src/endpoints/themes.js +++ b/src/endpoints/themes.js @@ -29,7 +29,7 @@ router.post('/delete', function (request, response) { console.error('Theme file not found:', filename); return response.sendStatus(404); } - fs.rmSync(filename); + fs.unlinkSync(filename); return response.sendStatus(200); } catch (error) { console.error(error); diff --git a/src/endpoints/thumbnails.js b/src/endpoints/thumbnails.js index 2e242ec17..583877127 100644 --- a/src/endpoints/thumbnails.js +++ b/src/endpoints/thumbnails.js @@ -75,7 +75,7 @@ export function invalidateThumbnail(directories, type, file) { const pathToThumbnail = path.join(folder, file); if (fs.existsSync(pathToThumbnail)) { - fs.rmSync(pathToThumbnail); + fs.unlinkSync(pathToThumbnail); } } diff --git a/src/endpoints/worldinfo.js b/src/endpoints/worldinfo.js index 0c043ea35..a989b6cb2 100644 --- a/src/endpoints/worldinfo.js +++ b/src/endpoints/worldinfo.js @@ -57,7 +57,7 @@ router.post('/delete', (request, response) => { throw new Error(`World info file ${filename} doesn't exist.`); } - fs.rmSync(pathToWorldInfo); + fs.unlinkSync(pathToWorldInfo); return response.sendStatus(200); }); From d3bb625efebec5805348df6bc05a855897bdd27d Mon Sep 17 00:00:00 2001 From: wickedcode Date: Thu, 1 May 2025 03:09:25 -0400 Subject: [PATCH 4/4] fix: recommend to use unlinkSync instead of rmSync, which has a better compatibility handling non-English characters --- src/endpoints/assets.js | 4 ++-- src/endpoints/avatars.js | 2 +- src/endpoints/backends/kobold.js | 2 +- src/endpoints/backgrounds.js | 6 +++--- src/endpoints/characters.js | 6 +++--- src/endpoints/chats.js | 4 ++-- src/endpoints/files.js | 2 +- src/endpoints/groups.js | 4 ++-- src/endpoints/openai.js | 2 +- src/endpoints/presets.js | 2 +- src/endpoints/sprites.js | 10 +++++----- src/util.js | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/endpoints/assets.js b/src/endpoints/assets.js index 439a62ec5..9420555a4 100644 --- a/src/endpoints/assets.js +++ b/src/endpoints/assets.js @@ -235,14 +235,14 @@ router.post('/download', async (request, response) => { const contentType = mime.lookup(temp_path) || 'application/octet-stream'; response.setHeader('Content-Type', contentType); response.send(fileContent); - fs.rmSync(temp_path); + fs.unlinkSync(temp_path); return; } // Move into asset place console.info('Download finished, moving file from', temp_path, 'to', file_path); fs.copyFileSync(temp_path, file_path); - fs.rmSync(temp_path); + fs.unlinkSync(temp_path); response.sendStatus(200); } catch (error) { diff --git a/src/endpoints/avatars.js b/src/endpoints/avatars.js index 4362a0058..6b665e8ee 100644 --- a/src/endpoints/avatars.js +++ b/src/endpoints/avatars.js @@ -53,7 +53,7 @@ router.post('/upload', async (request, response) => { const filename = request.body.overwrite_name || `${Date.now()}.png`; const pathToNewFile = path.join(request.user.directories.avatars, filename); writeFileAtomicSync(pathToNewFile, image); - fs.rmSync(pathToUpload); + fs.unlinkSync(pathToUpload); return response.send({ path: filename }); } catch (err) { return response.status(400).send('Is not a valid image'); diff --git a/src/endpoints/backends/kobold.js b/src/endpoints/backends/kobold.js index c0c158d62..44c5dd22c 100644 --- a/src/endpoints/backends/kobold.js +++ b/src/endpoints/backends/kobold.js @@ -204,7 +204,7 @@ router.post('/transcribe-audio', async function (request, response) { console.debug('Transcribing audio with KoboldCpp', server); const fileBase64 = fs.readFileSync(request.file.path).toString('base64'); - fs.rmSync(request.file.path); + fs.unlinkSync(request.file.path); const headers = {}; setAdditionalHeadersByType(headers, TEXTGEN_TYPES.KOBOLDCPP, server, request.user.directories); diff --git a/src/endpoints/backgrounds.js b/src/endpoints/backgrounds.js index 0559900eb..131bd1223 100644 --- a/src/endpoints/backgrounds.js +++ b/src/endpoints/backgrounds.js @@ -30,7 +30,7 @@ router.post('/delete', getFileNameValidationFunction('bg'), function (request, r return response.sendStatus(400); } - fs.rmSync(fileName); + fs.unlinkSync(fileName); invalidateThumbnail(request.user.directories, 'bg', request.body.bg); return response.send('ok'); }); @@ -52,7 +52,7 @@ router.post('/rename', function (request, response) { } fs.copyFileSync(oldFileName, newFileName); - fs.rmSync(oldFileName); + fs.unlinkSync(oldFileName); invalidateThumbnail(request.user.directories, 'bg', request.body.old_bg); return response.send('ok'); }); @@ -65,7 +65,7 @@ router.post('/upload', function (request, response) { try { fs.copyFileSync(img_path, path.join(request.user.directories.backgrounds, filename)); - fs.rmSync(img_path); + fs.unlinkSync(img_path); invalidateThumbnail(request.user.directories, 'bg', filename); response.send(filename); } catch (err) { diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index d5f549a86..59ab52fe2 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -720,7 +720,7 @@ function convertWorldInfoToCharacterBook(name, entries) { */ async function importFromYaml(uploadPath, context, preservedFileName) { const fileText = fs.readFileSync(uploadPath, 'utf8'); - fs.rmSync(uploadPath); + fs.unlinkSync(uploadPath); const yamlData = yaml.parse(fileText); console.info('Importing from YAML'); yamlData.name = sanitize(yamlData.name); @@ -754,7 +754,7 @@ async function importFromYaml(uploadPath, context, preservedFileName) { */ async function importFromCharX(uploadPath, { request }, preservedFileName) { const data = fs.readFileSync(uploadPath).buffer; - fs.rmSync(uploadPath); + fs.unlinkSync(uploadPath); console.info('Importing from CharX'); const cardBuffer = await extractFileFromZipBuffer(data, 'card.json'); @@ -995,7 +995,7 @@ router.post('/rename', validateAvatarUrlMiddleware, async function (request, res } // Remove the old character file - fs.rmSync(oldAvatarPath); + fs.unlinkSync(oldAvatarPath); // Return new avatar name to ST return response.send({ avatar: newAvatarName }); diff --git a/src/endpoints/chats.js b/src/endpoints/chats.js index 2762055a2..08309a170 100644 --- a/src/endpoints/chats.js +++ b/src/endpoints/chats.js @@ -433,7 +433,7 @@ router.post('/rename', validateAvatarUrlMiddleware, async function (request, res } fs.copyFileSync(pathToOriginalFile, pathToRenamedFile); - fs.rmSync(pathToOriginalFile); + fs.unlinkSync(pathToOriginalFile); console.info('Successfully renamed.'); return response.send({ ok: true, sanitizedFileName }); }); @@ -665,7 +665,7 @@ router.post('/group/delete', (request, response) => { const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`); if (fs.existsSync(pathToFile)) { - fs.rmSync(pathToFile); + fs.unlinkSync(pathToFile); return response.send({ ok: true }); } diff --git a/src/endpoints/files.js b/src/endpoints/files.js index 4238b5003..46abc82c9 100644 --- a/src/endpoints/files.js +++ b/src/endpoints/files.js @@ -66,7 +66,7 @@ router.post('/delete', async (request, response) => { return response.status(404).send('File not found'); } - fs.rmSync(pathToDelete); + fs.unlinkSync(pathToDelete); console.info(`Deleted file: ${request.body.path} from ${request.user.profile.handle}`); return response.sendStatus(200); } catch (error) { diff --git a/src/endpoints/groups.js b/src/endpoints/groups.js index d6c5bf249..469bddba6 100644 --- a/src/endpoints/groups.js +++ b/src/endpoints/groups.js @@ -117,7 +117,7 @@ router.post('/delete', async (request, response) => { const pathToFile = path.join(request.user.directories.groupChats, `${id}.jsonl`); if (fs.existsSync(pathToFile)) { - fs.rmSync(pathToFile); + fs.unlinkSync(pathToFile); } } } @@ -126,7 +126,7 @@ router.post('/delete', async (request, response) => { } if (fs.existsSync(pathToGroup)) { - fs.rmSync(pathToGroup); + fs.unlinkSync(pathToGroup); } return response.send({ ok: true }); diff --git a/src/endpoints/openai.js b/src/endpoints/openai.js index 8cbc1bb76..bb8c1a9d2 100644 --- a/src/endpoints/openai.js +++ b/src/endpoints/openai.js @@ -234,7 +234,7 @@ router.post('/transcribe-audio', async (request, response) => { return response.status(500).send(text); } - fs.rmSync(request.file.path); + fs.unlinkSync(request.file.path); const data = await result.json(); console.debug('OpenAI transcription response', data); return response.json(data); diff --git a/src/endpoints/presets.js b/src/endpoints/presets.js index 7c5db1fdb..ab57f355b 100644 --- a/src/endpoints/presets.js +++ b/src/endpoints/presets.js @@ -124,7 +124,7 @@ router.post('/delete-openai', function (request, response) { const pathToFile = path.join(request.user.directories.openAI_Settings, `${name}.json`); if (fs.existsSync(pathToFile)) { - fs.rmSync(pathToFile); + fs.unlinkSync(pathToFile); return response.send({ ok: true }); } diff --git a/src/endpoints/sprites.js b/src/endpoints/sprites.js index 15a21bff1..d9a380e65 100644 --- a/src/endpoints/sprites.js +++ b/src/endpoints/sprites.js @@ -165,7 +165,7 @@ router.post('/delete', async (request, response) => { // Remove existing sprite with the same label for (const file of files) { if (path.parse(file).name === spriteName) { - fs.rmSync(path.join(spritesPath, file)); + fs.unlinkSync(path.join(spritesPath, file)); } } @@ -206,7 +206,7 @@ router.post('/upload-zip', async (request, response) => { const existingFile = files.find(file => path.parse(file).name === path.parse(filename).name); if (existingFile) { - fs.rmSync(path.join(spritesPath, existingFile)); + fs.unlinkSync(path.join(spritesPath, existingFile)); } // Write sprite buffer to disk @@ -215,7 +215,7 @@ router.post('/upload-zip', async (request, response) => { } // Remove uploaded ZIP file - fs.rmSync(spritePackPath); + fs.unlinkSync(spritePackPath); return response.send({ count: sprites.length }); } catch (error) { console.error(error); @@ -251,7 +251,7 @@ router.post('/upload', async (request, response) => { // Remove existing sprite with the same label for (const file of files) { if (path.parse(file).name === spriteName) { - fs.rmSync(path.join(spritesPath, file)); + fs.unlinkSync(path.join(spritesPath, file)); } } @@ -261,7 +261,7 @@ router.post('/upload', async (request, response) => { // Copy uploaded file to sprites folder fs.cpSync(spritePath, pathToFile); // Remove uploaded file - fs.rmSync(spritePath); + fs.unlinkSync(spritePath); return response.sendStatus(200); } catch (error) { console.error(error); diff --git a/src/util.js b/src/util.js index ec94ceb1a..c1d1ed4b4 100644 --- a/src/util.js +++ b/src/util.js @@ -419,7 +419,7 @@ export function removeOldBackups(directory, prefix, limit = null) { break; } - fs.rmSync(oldest); + fs.unlinkSync(oldest); } } }