Adjust itemized prompts on message move/delete (#5000) * Adjust itemized prompts on message move/delete * Sort itemized prompts on swap * Remove itemized prompt on regeneration

684b755826a80d86ce9c65beb9dda1ced64563c8

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

Signed
2 files changed, +51 -2Ignore whitespace
public/script.js+8 -1
@@ -272,7 +272,7 @@ import { extractReasoningFromData, extractReasoningSignatureFromData, initReason
272import { accountStorage } from './scripts/util/AccountStorage.js';272import { accountStorage } from './scripts/util/AccountStorage.js';
273import { initWelcomeScreen, openPermanentAssistantChat, openPermanentAssistantCard, getPermanentAssistantAvatar } from './scripts/welcome-screen.js';273import { initWelcomeScreen, openPermanentAssistantChat, openPermanentAssistantCard, getPermanentAssistantAvatar } from './scripts/welcome-screen.js';
274import { initDataMaid } from './scripts/data-maid.js';274import { initDataMaid } from './scripts/data-maid.js';
275import { clearItemizedPrompts, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts } from './scripts/itemized-prompts.js';275import { clearItemizedPrompts, deleteItemizedPromptForMessage, deleteItemizedPrompts, findItemizedPromptSet, initItemizedPrompts, itemizedParams, itemizedPrompts, loadItemizedPrompts, promptItemize, replaceItemizedPromptText, saveItemizedPrompts, swapItemizedPrompts } from './scripts/itemized-prompts.js';
276import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMessage, system_message_types, system_messages } from './scripts/system-messages.js';276import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMessage, system_message_types, system_messages } from './scripts/system-messages.js';
277import { event_types, eventSource } from './scripts/events.js';277import { event_types, eventSource } from './scripts/events.js';
278import { initAccessibility } from './scripts/a11y.js';278import { initAccessibility } from './scripts/a11y.js';
@@ -1546,6 +1546,7 @@ export async function clearChat() {
1546}1546}
15471547
1548export async function deleteLastMessage() {1548export async function deleteLastMessage() {
1549 deleteItemizedPromptForMessage(chat.length - 1);
1549 chat.length = chat.length - 1;1550 chat.length = chat.length - 1;
1550 chatElement.children('.mes').last().remove();1551 chatElement.children('.mes').last().remove();
1551 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);1552 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
@@ -1601,6 +1602,7 @@ export async function deleteMessage(id, swipeDeletionIndex = undefined, askConfi
1601 chat_metadata.tainted = true;1602 chat_metadata.tainted = true;
16021603
1603 const startIndex = [0, minId].includes(id) ? id : null;1604 const startIndex = [0, minId].includes(id) ? id : null;
1605 deleteItemizedPromptForMessage(id);
1604 updateViewMessageIds(startIndex);1606 updateViewMessageIds(startIndex);
1605 saveChatDebounced();1607 saveChatDebounced();
16061608
@@ -4230,6 +4232,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4230 //do nothing? why does this check exist?4232 //do nothing? why does this check exist?
4231 }4233 }
4232 else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) {4234 else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) {
4235 deleteItemizedPromptForMessage(chat.length - 1);
4233 chat.length = chat.length - 1;4236 chat.length = chat.length - 1;
4234 await removeLastMessage();4237 await removeLastMessage();
4235 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);4238 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
@@ -8142,6 +8145,7 @@ async function messageEditMove(sourceId, targetId) {
8142 this_edit_mes_id = targetId;8145 this_edit_mes_id = targetId;
8143 }8146 }
81448147
8148 swapItemizedPrompts(sourceId, targetId);
8145 updateViewMessageIds();8149 updateViewMessageIds();
8146 refreshSwipeButtons();8150 refreshSwipeButtons();
8147 await saveChatConditional();8151 await saveChatConditional();
@@ -11450,6 +11454,9 @@ jQuery(async function () {
11450 });11454 });
1145111455
11452 if (this_del_mes >= 0) {11456 if (this_del_mes >= 0) {
11457 for (let i = (chat.length - 1); i >= this_del_mes; i--) {
11458 deleteItemizedPromptForMessage(i);
11459 }
11453 chatElement.find(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();11460 chatElement.find(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();
11454 chatElement.find(`.mes[mesid="${this_del_mes}"]`).remove();11461 chatElement.find(`.mes[mesid="${this_del_mes}"]`).remove();
11455 chat.length = this_del_mes;11462 chat.length = this_del_mes;
public/scripts/itemized-prompts.js+43 -1
@@ -222,6 +222,7 @@ export async function itemizedParams(itemizedPrompts, thisPromptSet, incomingMes
222222
223export function findItemizedPromptSet(itemizedPrompts, incomingMesId) {223export function findItemizedPromptSet(itemizedPrompts, incomingMesId) {
224 let thisPromptSet = undefined;224 let thisPromptSet = undefined;
225 priorPromptArrayItemForRawPromptDisplay = -1;
225226
226 for (let i = 0; i < itemizedPrompts.length; i++) {227 for (let i = 0; i < itemizedPrompts.length; i++) {
227 console.log(`looking for ${incomingMesId} vs ${itemizedPrompts[i].mesId}`);228 console.log(`looking for ${incomingMesId} vs ${itemizedPrompts[i].mesId}`);
@@ -262,7 +263,7 @@ export async function promptItemize(itemizedPrompts, requestedMesId) {
262263
263 /** @type {HTMLElement} */264 /** @type {HTMLElement} */
264 const diffPrevPrompt = popup.dlg.querySelector('#diffPrevPrompt');265 const diffPrevPrompt = popup.dlg.querySelector('#diffPrevPrompt');
265 if (priorPromptArrayItemForRawPromptDisplay) {266 if (priorPromptArrayItemForRawPromptDisplay >= 0) {
266 diffPrevPrompt.style.display = '';267 diffPrevPrompt.style.display = '';
267 diffPrevPrompt.addEventListener('click', function () {268 diffPrevPrompt.addEventListener('click', function () {
268 const dmp = new DiffMatchPatch();269 const dmp = new DiffMatchPatch();
@@ -350,3 +351,44 @@ export function initItemizedPrompts() {
350 await deleteItemizedPrompts(name);351 await deleteItemizedPrompts(name);
351 });352 });
352}353}
354
355/**
356 * Swaps the itemized prompts between two messages. Useful when moving messages around in the chat.
357 * @param {number} sourceMessageId Source message ID
358 * @param {number} targetMessageId Target message ID
359 */
360export function swapItemizedPrompts(sourceMessageId, targetMessageId) {
361 if (!Array.isArray(itemizedPrompts)) {
362 return;
363 }
364
365 const sourcePrompts = itemizedPrompts.filter(x => x.mesId === sourceMessageId);
366 const targetPrompts = itemizedPrompts.filter(x => x.mesId === targetMessageId);
367
368 sourcePrompts.forEach(prompt => {
369 prompt.mesId = targetMessageId;
370 });
371
372 targetPrompts.forEach(prompt => {
373 prompt.mesId = sourceMessageId;
374 });
375
376 itemizedPrompts.sort((a, b) => a.mesId - b.mesId);
377}
378
379/**
380 * Deletes the itemized prompt for a specific message.
381 * Shifts down other itemized prompts as necessary.
382 * @param {number} messageId Message ID to delete itemized prompt for
383 */
384export function deleteItemizedPromptForMessage(messageId) {
385 if (!Array.isArray(itemizedPrompts)) {
386 return;
387 }
388
389 itemizedPrompts = itemizedPrompts.filter(x => x.mesId !== messageId);
390
391 for (const prompt of itemizedPrompts.filter(x => x.mesId > messageId)) {
392 prompt.mesId -= 1;
393 }
394}