From c5f251c6e376701a273876affaa7a87910420453 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Wed, 26 Mar 2025 22:35:10 +0300 Subject: [PATCH 01/22] Added stop string cleanup, better stopping string param --- public/scripts/custom-request.js | 72 ++++++++++++++++++++++++++++---- public/scripts/instruct-mode.js | 28 +++++++------ 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/public/scripts/custom-request.js b/public/scripts/custom-request.js index 250e74f11..d6281a57a 100644 --- a/public/scripts/custom-request.js +++ b/public/scripts/custom-request.js @@ -2,7 +2,7 @@ import { getPresetManager } from './preset-manager.js'; import { extractMessageFromData, getGenerateUrl, getRequestHeaders } from '../script.js'; import { getTextGenServer } from './textgen-settings.js'; import { extractReasoningFromData } from './reasoning.js'; -import { formatInstructModeChat, formatInstructModePrompt, names_behavior_types } from './instruct-mode.js'; +import { formatInstructModeChat, formatInstructModePrompt, getInstructStoppingSequences, names_behavior_types } from './instruct-mode.js'; import { getStreamingReply, tryParseStreamingError } from './openai.js'; import EventSourceStream from './sse-stream.js'; @@ -222,10 +222,13 @@ export class TextCompletionService { } } + + /** @type {InstructSettings | undefined} */ + let instructPreset; // Handle instruct formatting if requested if (Array.isArray(prompt) && instructName) { const instructPresetManager = getPresetManager('instruct'); - let instructPreset = instructPresetManager?.getCompletionPresetByName(instructName); + instructPreset = instructPresetManager?.getCompletionPresetByName(instructName); if (instructPreset) { // Clone the preset to avoid modifying the original instructPreset = structuredClone(instructPreset); @@ -266,10 +269,9 @@ export class TextCompletionService { formattedMessages.push(messageContent); } requestData.prompt = formattedMessages.join(''); - if (instructPreset.output_suffix) { - requestData.stop = [instructPreset.output_suffix]; - requestData.stopping_strings = [instructPreset.output_suffix]; - } + const stoppingStrings = getInstructStoppingSequences({ customInstruct: instructPreset, useStopString: false }); + requestData.stop = stoppingStrings + requestData.stopping_strings = stoppingStrings; } else { console.warn(`Instruct preset "${instructName}" not found, using basic formatting`); requestData.prompt = prompt.map(x => x.content).join('\n\n'); @@ -283,7 +285,63 @@ export class TextCompletionService { // @ts-ignore const data = this.createRequestData(requestData); - return await this.sendRequest(data, extractData, signal); + const response = await this.sendRequest(data, extractData, signal); + // Remove stopping strings from the end + if (!data.stream && extractData) { + /** @type {ExtractedData} */ + // @ts-ignore + const extractedData = response; + + let message = extractedData.content; + + message = message.replace(/[^\S\r\n]+$/gm, ''); + + if (requestData.stopping_strings) { + for (const stoppingString of requestData.stopping_strings) { + if (stoppingString.length) { + for (let j = stoppingString.length; j > 0; j--) { + if (message.slice(-j) === stoppingString.slice(0, j)) { + message = message.slice(0, -j); + break; + } + } + } + } + } + + if (instructPreset) { + if (instructPreset.stop_sequence) { + const index = message.indexOf(instructPreset.stop_sequence); + if (index != -1) { + message = message.substring(0, index); + } + } + if (instructPreset.input_sequence && instructPreset.input_sequence.trim()) { + const index = message.indexOf(instructPreset.input_sequence); + if (index != -1) { + message = message.substring(0, index); + } + } + if (instructPreset.output_sequence) { + instructPreset.output_sequence.split('\n') + .filter(line => line.trim() !== '') + .forEach(line => { + message = message.replaceAll(line, ''); + }); + } + if (instructPreset.last_output_sequence) { + instructPreset.last_output_sequence.split('\n') + .filter(line => line.trim() !== '') + .forEach(line => { + message = message.replaceAll(line, ''); + }); + } + } + + extractedData.content = message; + } + + return response; } /** diff --git a/public/scripts/instruct-mode.js b/public/scripts/instruct-mode.js index 000d242b0..f5b93dd45 100644 --- a/public/scripts/instruct-mode.js +++ b/public/scripts/instruct-mode.js @@ -243,9 +243,12 @@ export function autoSelectInstructPreset(modelId) { /** * Converts instruct mode sequences to an array of stopping strings. + * @param {{customInstruct?: InstructSettings, useStopString?: boolean}} options * @returns {string[]} Array of instruct mode stopping strings. */ -export function getInstructStoppingSequences() { +export function getInstructStoppingSequences({ customInstruct = null, useStopString = false } = {}) { + const instruct = structuredClone(customInstruct ?? power_user.instruct); + /** * Adds instruct mode sequence to the result array. * @param {string} sequence Sequence string. @@ -254,7 +257,7 @@ export function getInstructStoppingSequences() { function addInstructSequence(sequence) { // Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string // But it's a problem for Metharme which doesn't use newlines to separate them. - const wrap = (s) => power_user.instruct.wrap ? '\n' + s : s; + const wrap = (s) => instruct.wrap ? '\n' + s : s; // Sequence must be a non-empty string if (typeof sequence === 'string' && sequence.length > 0) { // If sequence is just a whitespace or newline - we don't want to make it a stopping string @@ -262,7 +265,7 @@ export function getInstructStoppingSequences() { if (sequence.trim().length > 0) { const wrappedSequence = wrap(sequence); // Need to respect "insert macro" setting - const stopString = power_user.instruct.macro ? substituteParams(wrappedSequence) : wrappedSequence; + const stopString = instruct.macro ? substituteParams(wrappedSequence) : wrappedSequence; result.push(stopString); } } @@ -270,14 +273,15 @@ export function getInstructStoppingSequences() { const result = []; - if (power_user.instruct.enabled) { - const stop_sequence = power_user.instruct.stop_sequence || ''; - const input_sequence = power_user.instruct.input_sequence?.replace(/{{name}}/gi, name1) || ''; - const output_sequence = power_user.instruct.output_sequence?.replace(/{{name}}/gi, name2) || ''; - const first_output_sequence = power_user.instruct.first_output_sequence?.replace(/{{name}}/gi, name2) || ''; - const last_output_sequence = power_user.instruct.last_output_sequence?.replace(/{{name}}/gi, name2) || ''; - const system_sequence = power_user.instruct.system_sequence?.replace(/{{name}}/gi, 'System') || ''; - const last_system_sequence = power_user.instruct.last_system_sequence?.replace(/{{name}}/gi, 'System') || ''; + // Since preset's don't have "enabled", we assume it's always enabled + if (customInstruct ?? instruct.enabled) { + const stop_sequence = instruct.stop_sequence || ''; + const input_sequence = instruct.input_sequence?.replace(/{{name}}/gi, name1) || ''; + const output_sequence = instruct.output_sequence?.replace(/{{name}}/gi, name2) || ''; + const first_output_sequence = instruct.first_output_sequence?.replace(/{{name}}/gi, name2) || ''; + const last_output_sequence = instruct.last_output_sequence?.replace(/{{name}}/gi, name2) || ''; + const system_sequence = instruct.system_sequence?.replace(/{{name}}/gi, 'System') || ''; + const last_system_sequence = instruct.last_system_sequence?.replace(/{{name}}/gi, 'System') || ''; const combined_sequence = [ stop_sequence, @@ -292,7 +296,7 @@ export function getInstructStoppingSequences() { combined_sequence.split('\n').filter((line, index, self) => self.indexOf(line) === index).forEach(addInstructSequence); } - if (power_user.context.use_stop_strings) { + if (useStopString ?? power_user.context.use_stop_strings) { if (power_user.context.chat_start) { result.push(`\n${substituteParams(power_user.context.chat_start)}`); } From a7d48b1aed317625bb5eab5aac8baa471883b374 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Wed, 26 Mar 2025 23:21:48 +0300 Subject: [PATCH 02/22] Added overridable instruct settings, removed macro override --- public/scripts/custom-request.js | 7 +++++-- public/scripts/extensions/shared.js | 12 ++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/public/scripts/custom-request.js b/public/scripts/custom-request.js index d6281a57a..353e6cec0 100644 --- a/public/scripts/custom-request.js +++ b/public/scripts/custom-request.js @@ -190,6 +190,7 @@ export class TextCompletionService { * @param {Object} options - Configuration options * @param {string?} [options.presetName] - Name of the preset to use for generation settings * @param {string?} [options.instructName] - Name of instruct preset for message formatting + * @param {Partial?} [options.instructSettings] - Override instruct settings * @param {boolean} extractData - Whether to extract structured data from response * @param {AbortSignal?} [signal] * @returns {Promise AsyncGenerator)>} If not streaming, returns extracted data; if streaming, returns a function that creates an AsyncGenerator @@ -232,8 +233,10 @@ export class TextCompletionService { if (instructPreset) { // Clone the preset to avoid modifying the original instructPreset = structuredClone(instructPreset); - instructPreset.macro = false; instructPreset.names_behavior = names_behavior_types.NONE; + if (options.instructSettings) { + Object.assign(instructPreset, options.instructSettings); + } // Format messages using instruct formatting const formattedMessages = []; @@ -270,7 +273,7 @@ export class TextCompletionService { } requestData.prompt = formattedMessages.join(''); const stoppingStrings = getInstructStoppingSequences({ customInstruct: instructPreset, useStopString: false }); - requestData.stop = stoppingStrings + requestData.stop = stoppingStrings; requestData.stopping_strings = stoppingStrings; } else { console.warn(`Instruct preset "${instructName}" not found, using basic formatting`); diff --git a/public/scripts/extensions/shared.js b/public/scripts/extensions/shared.js index 144ac3d6b..9ca630fa9 100644 --- a/public/scripts/extensions/shared.js +++ b/public/scripts/extensions/shared.js @@ -285,6 +285,7 @@ export class ConnectionManagerRequestService { extractData: true, includePreset: true, includeInstruct: true, + instructSettings: {}, }; static getAllowedTypes() { @@ -298,11 +299,17 @@ export class ConnectionManagerRequestService { * @param {string} profileId * @param {string | (import('../custom-request.js').ChatCompletionMessage & {ignoreInstruct?: boolean})[]} prompt * @param {number} maxTokens - * @param {{stream?: boolean, signal?: AbortSignal, extractData?: boolean, includePreset?: boolean, includeInstruct?: boolean}} custom - default values are true + * @param {object} custom + * @param {boolean?} [custom.stream=false] + * @param {AbortSignal?} [custom.signal] + * @param {boolean?} [custom.extractData=true] + * @param {boolean?} [custom.includePreset=true] + * @param {boolean?} [custom.includeInstruct=true] + * @param {Partial?} [custom.instructSettings] Override instruct settings * @returns {Promise AsyncGenerator)>} If not streaming, returns extracted data; if streaming, returns a function that creates an AsyncGenerator */ static async sendRequest(profileId, prompt, maxTokens, custom = this.defaultSendRequestParams) { - const { stream, signal, extractData, includePreset, includeInstruct } = { ...this.defaultSendRequestParams, ...custom }; + const { stream, signal, extractData, includePreset, includeInstruct, instructSettings } = { ...this.defaultSendRequestParams, ...custom }; const context = SillyTavern.getContext(); if (context.extensionSettings.disabledExtensions.includes('connection-manager')) { @@ -346,6 +353,7 @@ export class ConnectionManagerRequestService { }, { instructName: includeInstruct ? profile.instruct : undefined, presetName: includePreset ? profile.preset : undefined, + instructSettings: includeInstruct ? instructSettings : undefined, }, extractData, signal); } default: { From 972b1e5fa7eb78fa0f5e858774c1889757cf041c Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Wed, 26 Mar 2025 23:30:09 +0300 Subject: [PATCH 03/22] Fixed variable naming, better jsdoc --- public/scripts/custom-request.js | 2 +- public/scripts/extensions/shared.js | 2 +- public/scripts/instruct-mode.js | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/public/scripts/custom-request.js b/public/scripts/custom-request.js index 353e6cec0..7ea8f60eb 100644 --- a/public/scripts/custom-request.js +++ b/public/scripts/custom-request.js @@ -272,7 +272,7 @@ export class TextCompletionService { formattedMessages.push(messageContent); } requestData.prompt = formattedMessages.join(''); - const stoppingStrings = getInstructStoppingSequences({ customInstruct: instructPreset, useStopString: false }); + const stoppingStrings = getInstructStoppingSequences({ customInstruct: instructPreset, useStopStrings: false }); requestData.stop = stoppingStrings; requestData.stopping_strings = stoppingStrings; } else { diff --git a/public/scripts/extensions/shared.js b/public/scripts/extensions/shared.js index 9ca630fa9..e4cca98fa 100644 --- a/public/scripts/extensions/shared.js +++ b/public/scripts/extensions/shared.js @@ -299,7 +299,7 @@ export class ConnectionManagerRequestService { * @param {string} profileId * @param {string | (import('../custom-request.js').ChatCompletionMessage & {ignoreInstruct?: boolean})[]} prompt * @param {number} maxTokens - * @param {object} custom + * @param {Object} custom * @param {boolean?} [custom.stream=false] * @param {AbortSignal?} [custom.signal] * @param {boolean?} [custom.extractData=true] diff --git a/public/scripts/instruct-mode.js b/public/scripts/instruct-mode.js index f5b93dd45..7edfb1e0b 100644 --- a/public/scripts/instruct-mode.js +++ b/public/scripts/instruct-mode.js @@ -243,10 +243,12 @@ export function autoSelectInstructPreset(modelId) { /** * Converts instruct mode sequences to an array of stopping strings. - * @param {{customInstruct?: InstructSettings, useStopString?: boolean}} options + * @param {Object} options + * @param {InstructSettings?} [options.customInstruct=null] - Custom instruct settings. + * @param {boolean?} [options.useStopStrings=false] - Decides whether to use "Chat Start" and "Example Separator" * @returns {string[]} Array of instruct mode stopping strings. */ -export function getInstructStoppingSequences({ customInstruct = null, useStopString = false } = {}) { +export function getInstructStoppingSequences({ customInstruct = null, useStopStrings = false } = {}) { const instruct = structuredClone(customInstruct ?? power_user.instruct); /** @@ -296,7 +298,7 @@ export function getInstructStoppingSequences({ customInstruct = null, useStopStr combined_sequence.split('\n').filter((line, index, self) => self.indexOf(line) === index).forEach(addInstructSequence); } - if (useStopString ?? power_user.context.use_stop_strings) { + if (useStopStrings ?? power_user.context.use_stop_strings) { if (power_user.context.chat_start) { result.push(`\n${substituteParams(power_user.context.chat_start)}`); } From 4e207c2cf0123f84c9438512bef211cb8dd53598 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Wed, 26 Mar 2025 23:38:02 +0300 Subject: [PATCH 04/22] Removed duplicate codes --- public/scripts/custom-request.js | 46 +++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/public/scripts/custom-request.js b/public/scripts/custom-request.js index 7ea8f60eb..5cd3f4827 100644 --- a/public/scripts/custom-request.js +++ b/public/scripts/custom-request.js @@ -313,32 +313,30 @@ export class TextCompletionService { } if (instructPreset) { - if (instructPreset.stop_sequence) { - const index = message.indexOf(instructPreset.stop_sequence); - if (index != -1) { - message = message.substring(0, index); + [ + instructPreset.stop_sequence, + instructPreset.input_sequence, + ].forEach(sequence => { + if (sequence?.trim()) { + const index = message.indexOf(sequence); + if (index !== -1) { + message = message.substring(0, index); + } } - } - if (instructPreset.input_sequence && instructPreset.input_sequence.trim()) { - const index = message.indexOf(instructPreset.input_sequence); - if (index != -1) { - message = message.substring(0, index); + }); + + [ + instructPreset.output_sequence, + instructPreset.last_output_sequence, + ].forEach(sequences => { + if (sequences) { + sequences.split('\n') + .filter(line => line.trim() !== '') + .forEach(line => { + message = message.replaceAll(line, ''); + }); } - } - if (instructPreset.output_sequence) { - instructPreset.output_sequence.split('\n') - .filter(line => line.trim() !== '') - .forEach(line => { - message = message.replaceAll(line, ''); - }); - } - if (instructPreset.last_output_sequence) { - instructPreset.last_output_sequence.split('\n') - .filter(line => line.trim() !== '') - .forEach(line => { - message = message.replaceAll(line, ''); - }); - } + }); } extractedData.content = message; From f14c73bfccd10d9edb7f50c05e5146292dca2465 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Thu, 27 Mar 2025 04:48:17 +0300 Subject: [PATCH 05/22] Added move button for lorebook entries --- public/index.html | 1 + public/scripts/world-info.js | 171 +++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) diff --git a/public/index.html b/public/index.html index 12ae48143..4c8b40d29 100644 --- a/public/index.html +++ b/public/index.html @@ -6013,6 +6013,7 @@ + diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 07625437a..35e4b064e 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -3139,6 +3139,70 @@ export async function getWorldEntry(name, data, entry) { updateEditor(navigation_option.previous); }); + // move button + const moveButton = template.find('.move_entry_button'); + moveButton.data('uid', entry.uid); + moveButton.data('current-world', name); + moveButton.on('click', async function (e) { + e.stopPropagation(); + const sourceUid = $(this).data('uid'); + const sourceWorld = $(this).data('current-world'); + // Loading world info is bad, do we have cache variable? + const sourceName = (await loadWorldInfo(sourceWorld)).entries[sourceUid].comment; + + let optionsHtml = ``; + let selectableWorldCount = 0; + world_names.forEach(worldName => { + if (worldName !== sourceWorld) { // Exclude the current world + optionsHtml += ``; + selectableWorldCount += 1; + } + }); + + if (selectableWorldCount === 0) { + toastr.warning(t`There are no other lorebooks to move to.`); + return; + } + + const content = ` +
${t`Move ${sourceName} to:`}
+ + `; + + const popupPromise = callGenericPopup(content, POPUP_TYPE.CONFIRM, '', { + okButton: t`Move`, + cancelButton: t`Cancel`, + }); + + let selectedWorldIndex = -1; + $('#move_entry_target_select').on('change', function () { + /** @type {string} */ + // @ts-ignore + const value = $(this).val(); + selectedWorldIndex = value === '' ? -1 : Number(value); + }); + + const popupConfirm = await popupPromise; + if (!popupConfirm) { + return; + } + + if (selectedWorldIndex === -1) { + return; + } + + const selectedValue = world_names[selectedWorldIndex]; + + if (!selectedValue) { + toastr.warning(t`Please select a target lorebook.`); + return; + } + + await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid); + }); + // scan depth const scanDepthInput = template.find('input[name="scanDepth"]'); scanDepthInput.data('uid', entry.uid); @@ -5267,3 +5331,110 @@ jQuery(() => { }); }); }); + +/** + * Moves a World Info entry from a source lorebook to a target lorebook. + * + * @param {string} sourceName - The name of the source lorebook file. + * @param {string} targetName - The name of the target lorebook file. + * @param {number|string} uid - The UID of the entry to move from the source lorebook. + * @returns {Promise} True if the move was successful, false otherwise. + */ +export async function moveWorldInfoEntry(sourceName, targetName, uid) { + console.log(`[WI] Attempting to move entry UID ${uid} from '${sourceName}' to '${targetName}'`); + + if (!sourceName || !targetName || uid === undefined || uid === null) { + console.error('[WI Move] Missing required arguments.'); + return false; + } + + if (sourceName === targetName) { + toastr.warning(t`Source and target lorebooks cannot be the same.`); + return false; + } + + if (!world_names.includes(sourceName)) { + toastr.error(t`Source lorebook '${sourceName}' not found.`); + console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`); + return false; + } + + if (!world_names.includes(targetName)) { + toastr.error(t`Target lorebook '${targetName}' not found.`); + console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`); + return false; + } + + const entryUidString = String(uid); + + try { + const sourceData = await loadWorldInfo(sourceName); + const targetData = await loadWorldInfo(targetName); + + if (!sourceData || !sourceData.entries) { + toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`); + console.error(`[WI Move] Could not load source data for '${sourceName}'.`); + return false; + } + if (!targetData || !targetData.entries) { + toastr.error(t`Failed to load data for target lorebook '${targetName}'.`); + console.error(`[WI Move] Could not load target data for '${targetName}'.`); + return false; + } + + if (!sourceData.entries[entryUidString]) { + toastr.error(t`Entry not found in source lorebook '${sourceName}'.`); + console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`); + return false; + } + + const entryToMove = structuredClone(sourceData.entries[entryUidString]); + + + const newUid = getFreeWorldEntryUid(targetData); + if (newUid === null) { + console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`); + return false; + } + + entryToMove.uid = newUid; + // Reset displayIndex or let it be recalculated based on target book's sorting? + // For simplicity, let's assign a high index initially, assuming it might be sorted later. + // Or maybe better, find the max displayIndex in target and add 1? + const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1); + entryToMove.displayIndex = maxDisplayIndex + 1; + + targetData.entries[newUid] = entryToMove; + + delete sourceData.entries[entryUidString]; + // Remove from originalData if it exists, using the original UID + deleteWIOriginalDataValue(sourceData, entryUidString); + console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); + + + // Save immediately to reduce chances of inconsistency if the browser is closed + // Note: This is not truly atomic. If one save fails, state could be inconsistent. + await saveWorldInfo(targetName, targetData, true); + console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); + await saveWorldInfo(sourceName, sourceData, true); + console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); + + + toastr.success(t`${entryToMove.comment} moved successfully!`); + + // Check if the currently viewed book in the editor is the source or target and reload it + const currentEditorBookIndex = Number($('#world_editor_select').val()); + if (!isNaN(currentEditorBookIndex)) { + const currentEditorBookName = world_names[currentEditorBookIndex]; + if (currentEditorBookName === sourceName || currentEditorBookName === targetName) { + reloadEditor(currentEditorBookName); + } + } + + return true; + } catch (error) { + toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`); + console.error('[WI Move] Unexpected error:', error); + return false; + } +} From bc08d42d0e85db81e5b858267af02121bf63a100 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Thu, 27 Mar 2025 06:21:52 +0300 Subject: [PATCH 06/22] Progress on move entry * Fixed header alignment * Building HTML with browser api instead of string. * jqueryElement.data(key, value) usages converted to jqueryElement.attr(data-key, value) * Logs simplified * Removed success toastry message --- .../templates/worldInfoKeywordHeaders.html | 2 +- public/scripts/world-info.js | 87 ++++++++++--------- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/public/scripts/templates/worldInfoKeywordHeaders.html b/public/scripts/templates/worldInfoKeywordHeaders.html index ec3eb946b..cf0c7f358 100644 --- a/public/scripts/templates/worldInfoKeywordHeaders.html +++ b/public/scripts/templates/worldInfoKeywordHeaders.html @@ -1,4 +1,4 @@ -
+
Title/Memo Strategy Position diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 35e4b064e..52b08ab82 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -2208,7 +2208,7 @@ function verifyWorldInfoSearchSortRule() { * Use `originalWIDataKeyMap` to find the correct value to be set. * * @param {object} data - The data object containing the original data entries. - * @param {string} uid - The unique identifier of the data entry. + * @param {number} uid - The unique identifier of the data entry. * @param {string} key - The key of the value to be set. * @param {any} value - The value to be set. */ @@ -3141,21 +3141,38 @@ export async function getWorldEntry(name, data, entry) { // move button const moveButton = template.find('.move_entry_button'); - moveButton.data('uid', entry.uid); - moveButton.data('current-world', name); + moveButton.attr('data-uid', entry.uid); + moveButton.attr('data-current-world', name); moveButton.on('click', async function (e) { e.stopPropagation(); const sourceUid = $(this).data('uid'); const sourceWorld = $(this).data('current-world'); - // Loading world info is bad, do we have cache variable? - const sourceName = (await loadWorldInfo(sourceWorld)).entries[sourceUid].comment; + const sourceWorldInfo = await loadWorldInfo(sourceWorld); + if (!sourceWorldInfo) { + return; + } + const sourceName = sourceWorldInfo.entries[sourceUid]?.comment; + if (sourceName === undefined) { + return; + } + + const select = document.createElement('select'); + select.id = 'move_entry_target_select'; + select.classList.add('text_pole', 'wide100p', 'margin-top'); + + const defaultOption = document.createElement('option'); + defaultOption.value = ''; + defaultOption.textContent = `-- ${t`Select Target Lorebook`} --`; + select.appendChild(defaultOption); - let optionsHtml = ``; let selectableWorldCount = 0; world_names.forEach(worldName => { - if (worldName !== sourceWorld) { // Exclude the current world - optionsHtml += ``; - selectableWorldCount += 1; + if (worldName !== sourceWorld) { // Exclude current world + const option = document.createElement('option'); + option.value = world_names.indexOf(worldName).toString(); + option.textContent = worldName; + select.appendChild(option); + selectableWorldCount++; } }); @@ -3164,27 +3181,24 @@ export async function getWorldEntry(name, data, entry) { return; } - const content = ` -
${t`Move ${sourceName} to:`}
- - `; + // Create wrapper div + const wrapper = document.createElement('div'); + wrapper.textContent = t`Move ${sourceName} to:`; - const popupPromise = callGenericPopup(content, POPUP_TYPE.CONFIRM, '', { + // Create container and append elements + const container = document.createElement('div'); + container.appendChild(wrapper); + container.appendChild(select); + + let selectedWorldIndex = -1; + select.addEventListener('change', function() { + selectedWorldIndex = this.value === '' ? -1 : Number(this.value); + }); + + const popupConfirm = await callGenericPopup(container, POPUP_TYPE.CONFIRM, '', { okButton: t`Move`, cancelButton: t`Cancel`, }); - - let selectedWorldIndex = -1; - $('#move_entry_target_select').on('change', function () { - /** @type {string} */ - // @ts-ignore - const value = $(this).val(); - selectedWorldIndex = value === '' ? -1 : Number(value); - }); - - const popupConfirm = await popupPromise; if (!popupConfirm) { return; } @@ -5337,19 +5351,11 @@ jQuery(() => { * * @param {string} sourceName - The name of the source lorebook file. * @param {string} targetName - The name of the target lorebook file. - * @param {number|string} uid - The UID of the entry to move from the source lorebook. + * @param {number} uid - The UID of the entry to move from the source lorebook. * @returns {Promise} True if the move was successful, false otherwise. */ export async function moveWorldInfoEntry(sourceName, targetName, uid) { - console.log(`[WI] Attempting to move entry UID ${uid} from '${sourceName}' to '${targetName}'`); - - if (!sourceName || !targetName || uid === undefined || uid === null) { - console.error('[WI Move] Missing required arguments.'); - return false; - } - if (sourceName === targetName) { - toastr.warning(t`Source and target lorebooks cannot be the same.`); return false; } @@ -5407,20 +5413,19 @@ export async function moveWorldInfoEntry(sourceName, targetName, uid) { targetData.entries[newUid] = entryToMove; delete sourceData.entries[entryUidString]; - // Remove from originalData if it exists, using the original UID + // Remove from originalData if it exists deleteWIOriginalDataValue(sourceData, entryUidString); + // TODO: setWIOriginalDataValue console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); - // Save immediately to reduce chances of inconsistency if the browser is closed - // Note: This is not truly atomic. If one save fails, state could be inconsistent. - await saveWorldInfo(targetName, targetData, true); + await saveWorldInfo(targetName, targetData); console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); - await saveWorldInfo(sourceName, sourceData, true); + await saveWorldInfo(sourceName, sourceData); console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); - toastr.success(t`${entryToMove.comment} moved successfully!`); + console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`); // Check if the currently viewed book in the editor is the source or target and reload it const currentEditorBookIndex = Number($('#world_editor_select').val()); From 8970c8274c502b76d3fe6530f8abbd5ee035c399 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Thu, 27 Mar 2025 14:38:54 +0300 Subject: [PATCH 07/22] Removed jqueryElement.data usages --- public/scripts/world-info.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 52b08ab82..e5b928281 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -3145,8 +3145,8 @@ export async function getWorldEntry(name, data, entry) { moveButton.attr('data-current-world', name); moveButton.on('click', async function (e) { e.stopPropagation(); - const sourceUid = $(this).data('uid'); - const sourceWorld = $(this).data('current-world'); + const sourceUid = $(this).attr('data-uid'); + const sourceWorld = $(this).attr('data-current-world'); const sourceWorldInfo = await loadWorldInfo(sourceWorld); if (!sourceWorldInfo) { return; From f33b31dc19490d90a089e90f7171205529604149 Mon Sep 17 00:00:00 2001 From: bmen25124 Date: Thu, 27 Mar 2025 14:42:50 +0300 Subject: [PATCH 08/22] Removed type from icon element --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 4c8b40d29..42f669843 100644 --- a/public/index.html +++ b/public/index.html @@ -6013,7 +6013,7 @@
- + From ae050be2d14d3d8a06f611e3350350990e134c7b Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:02:24 +0200 Subject: [PATCH 09/22] Lint fix --- public/scripts/extensions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index b03cddb9a..7c95bff21 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1230,7 +1230,7 @@ async function checkForExtensionUpdates(force) { for (const [id, manifest] of Object.entries(manifests)) { const isDisabled = extension_settings.disabledExtensions.includes(id); if (isDisabled) { - console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`) + console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`); continue; } const isGlobal = getExtensionType(id) === 'global'; @@ -1277,7 +1277,7 @@ async function autoUpdateExtensions(forceAll) { for (const [id, manifest] of Object.entries(manifests)) { const isDisabled = extension_settings.disabledExtensions.includes(id); if (!forceAll && isDisabled) { - console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`) + console.debug(`Skipping extension: ${manifest.display_name} (${id}) for non-admin user`); continue; } const isGlobal = getExtensionType(id) === 'global'; From e9c8b8c24e1cf7358e0f73fa186fbf146a22e0a4 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:17:53 +0200 Subject: [PATCH 10/22] Save both files immediately --- public/scripts/world-info.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index e5b928281..9bdab1914 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -5419,9 +5419,9 @@ export async function moveWorldInfoEntry(sourceName, targetName, uid) { console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); - await saveWorldInfo(targetName, targetData); + await saveWorldInfo(targetName, targetData, true); console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); - await saveWorldInfo(sourceName, sourceData); + await saveWorldInfo(sourceName, sourceData, true); console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); From 280608f061754082ec3b3f42ee18a7066481adc0 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:19:49 +0200 Subject: [PATCH 11/22] Use real class name --- public/scripts/world-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 9bdab1914..0cd342628 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -3158,7 +3158,7 @@ export async function getWorldEntry(name, data, entry) { const select = document.createElement('select'); select.id = 'move_entry_target_select'; - select.classList.add('text_pole', 'wide100p', 'margin-top'); + select.classList.add('text_pole', 'wide100p', 'marginTop10'); const defaultOption = document.createElement('option'); defaultOption.value = ''; From f4d467048b207129afb7325e0ee80c71cd0a89d8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:20:28 +0200 Subject: [PATCH 12/22] Fix type error --- public/scripts/world-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 0cd342628..2ea7c9875 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -5351,7 +5351,7 @@ jQuery(() => { * * @param {string} sourceName - The name of the source lorebook file. * @param {string} targetName - The name of the target lorebook file. - * @param {number} uid - The UID of the entry to move from the source lorebook. + * @param {string|number} uid - The UID of the entry to move from the source lorebook. * @returns {Promise} True if the move was successful, false otherwise. */ export async function moveWorldInfoEntry(sourceName, targetName, uid) { From 3ce715c52f6fccf9e38cf20c965707b584ee1ec3 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:24:39 +0200 Subject: [PATCH 13/22] Fix deletion from originalData --- public/scripts/world-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 2ea7c9875..e92562033 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -2232,7 +2232,7 @@ export function setWIOriginalDataValue(data, uid, key, value) { */ export function deleteWIOriginalDataValue(data, uid) { if (data.originalData && Array.isArray(data.originalData.entries)) { - const originalIndex = data.originalData.entries.findIndex(x => x.uid === uid); + const originalIndex = data.originalData.entries.findIndex(x => String(x.uid) === String(uid)); if (originalIndex >= 0) { data.originalData.entries.splice(originalIndex, 1); From 496f86e16eb409848511f974dab721c0e48085ee Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:27:23 +0200 Subject: [PATCH 14/22] Use non-strict equality instead to avoid explicit typecast --- public/scripts/world-info.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index e92562033..dbd2f230f 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -2232,7 +2232,9 @@ export function setWIOriginalDataValue(data, uid, key, value) { */ export function deleteWIOriginalDataValue(data, uid) { if (data.originalData && Array.isArray(data.originalData.entries)) { - const originalIndex = data.originalData.entries.findIndex(x => String(x.uid) === String(uid)); + // Non-strict equality is used here to allow for both string and number comparisons + // @eslint-disable-next-line eqeqeqeq + const originalIndex = data.originalData.entries.findIndex(x => x.uid == uid); if (originalIndex >= 0) { data.originalData.entries.splice(originalIndex, 1); From 5f00f2beb0f111fe8c5f52a6bd629ea48de128f8 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:28:14 +0200 Subject: [PATCH 15/22] Add quotes to item title being moved --- public/scripts/world-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index dbd2f230f..00c12034e 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -3185,7 +3185,7 @@ export async function getWorldEntry(name, data, entry) { // Create wrapper div const wrapper = document.createElement('div'); - wrapper.textContent = t`Move ${sourceName} to:`; + wrapper.textContent = t`Move "${sourceName}" to:`; // Create container and append elements const container = document.createElement('div'); From 561bed9cc27f7588cebc811f1d114887e6e8666e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:30:06 +0200 Subject: [PATCH 16/22] Replace goofy comment --- public/scripts/world-info.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 00c12034e..06dbe1c73 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -5406,9 +5406,7 @@ export async function moveWorldInfoEntry(sourceName, targetName, uid) { } entryToMove.uid = newUid; - // Reset displayIndex or let it be recalculated based on target book's sorting? - // For simplicity, let's assign a high index initially, assuming it might be sorted later. - // Or maybe better, find the max displayIndex in target and add 1? + // Place the entry at the end of the target lorebook const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1); entryToMove.displayIndex = maxDisplayIndex + 1; From 62395f409f8f1f1e8774c167cc58ffcaecec9420 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:34:30 +0200 Subject: [PATCH 17/22] There's no ==== operator (yet) --- public/scripts/world-info.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 06dbe1c73..19670dada 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -2233,7 +2233,7 @@ export function setWIOriginalDataValue(data, uid, key, value) { export function deleteWIOriginalDataValue(data, uid) { if (data.originalData && Array.isArray(data.originalData.entries)) { // Non-strict equality is used here to allow for both string and number comparisons - // @eslint-disable-next-line eqeqeqeq + // @eslint-disable-next-line eqeqeq const originalIndex = data.originalData.entries.findIndex(x => x.uid == uid); if (originalIndex >= 0) { From d93aba5706a084e90dee5b5e26ff85151a00d3d7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 27 Mar 2025 22:52:01 +0200 Subject: [PATCH 18/22] Fix useStopStrings defaulting --- public/scripts/instruct-mode.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/scripts/instruct-mode.js b/public/scripts/instruct-mode.js index 7edfb1e0b..56b16f560 100644 --- a/public/scripts/instruct-mode.js +++ b/public/scripts/instruct-mode.js @@ -245,10 +245,10 @@ export function autoSelectInstructPreset(modelId) { * Converts instruct mode sequences to an array of stopping strings. * @param {Object} options * @param {InstructSettings?} [options.customInstruct=null] - Custom instruct settings. - * @param {boolean?} [options.useStopStrings=false] - Decides whether to use "Chat Start" and "Example Separator" + * @param {boolean?} [options.useStopStrings] - Decides whether to use "Chat Start" and "Example Separator" * @returns {string[]} Array of instruct mode stopping strings. */ -export function getInstructStoppingSequences({ customInstruct = null, useStopStrings = false } = {}) { +export function getInstructStoppingSequences({ customInstruct = null, useStopStrings = null } = {}) { const instruct = structuredClone(customInstruct ?? power_user.instruct); /** From 6dc33e9637981a4effa9861effdcc63bf7ce559b Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 28 Mar 2025 01:25:13 +0100 Subject: [PATCH 19/22] Cast this_chid to number in personas.js Ensure numeric type for character ID comparisons Explicitly converts this_chid to Number in multiple functions to prevent type mismatch issues when checking character connections and persona states --- public/scripts/personas.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/public/scripts/personas.js b/public/scripts/personas.js index 591b8d6ab..4084aa537 100644 --- a/public/scripts/personas.js +++ b/public/scripts/personas.js @@ -465,7 +465,7 @@ export function initPersona(avatarId, personaName, personaDescription) { * @returns {Promise} A promise that resolves to true if the character was converted, false otherwise. */ export async function convertCharacterToPersona(characterId = null) { - if (null === characterId) characterId = this_chid; + if (null === characterId) characterId = Number(this_chid); const avatarUrl = characters[characterId]?.avatar; if (!avatarUrl) { @@ -1243,7 +1243,7 @@ function getPersonaStates(avatarId) { /** @type {PersonaConnection[]} */ const connections = power_user.persona_descriptions[avatarId]?.connections; const hasCharLock = !!connections?.some(c => - (!selected_group && c.type === 'character' && c.id === characters[this_chid]?.avatar) + (!selected_group && c.type === 'character' && c.id === characters[Number(this_chid)]?.avatar) || (selected_group && c.type === 'group' && c.id === selected_group)); return { @@ -1481,7 +1481,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { * @returns {string[]} - An array of persona keys that are connected to the given character key */ export function getConnectedPersonas(characterKey = undefined) { - characterKey ??= selected_group || characters[this_chid]?.avatar; + characterKey ??= selected_group || characters[Number(this_chid)]?.avatar; const connectedPersonas = Object.entries(power_user.persona_descriptions) .filter(([_, desc]) => desc.connections?.some(conn => conn.type === 'character' && conn.id === characterKey)) .map(([key, _]) => key); @@ -1513,7 +1513,7 @@ export async function showCharConnections() { console.log(`Unlocking persona ${personaId} from current character ${name2}`); power_user.persona_descriptions[personaId].connections = connections.filter(c => { if (menu_type == 'group_edit' && c.type == 'group' && c.id == selected_group) return false; - else if (c.type == 'character' && c.id == characters[this_chid]?.avatar) return false; + else if (c.type == 'character' && c.id == characters[Number(this_chid)]?.avatar) return false; return true; }); saveSettingsDebounced(); @@ -1545,8 +1545,8 @@ export async function showCharConnections() { export function getCurrentConnectionObj() { if (selected_group) return { type: 'group', id: selected_group }; - if (characters[this_chid]?.avatar) - return { type: 'character', id: characters[this_chid]?.avatar }; + if (characters[Number(this_chid)]?.avatar) + return { type: 'character', id: characters[Number(this_chid)]?.avatar }; return null; } @@ -1664,7 +1664,7 @@ async function syncUserNameToPersona() { * Only works if only the first message is present, and not in group mode. */ export function retriggerFirstMessageOnEmptyChat() { - if (this_chid >= 0 && !selected_group && chat.length === 1) { + if (Number(this_chid) >= 0 && !selected_group && chat.length === 1) { $('#firstmessage_textarea').trigger('input'); } } @@ -1979,4 +1979,3 @@ export async function initPersonas() { eventSource.on(event_types.CHAT_CHANGED, loadPersonaForCurrentChat); switchPersonaGridView(); } - From d95524032ecfea4db6484964901967c43fd94f0b Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 28 Mar 2025 01:28:36 +0100 Subject: [PATCH 20/22] Centralize first message retrigger on persona changes Moves retrigger logic to setUserAvatar entry point to avoid duplicate calls when updating personas Removes redundant triggers from name/avatar handlers Fixes #3774 --- public/scripts/personas.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/public/scripts/personas.js b/public/scripts/personas.js index 4084aa537..b302c37a0 100644 --- a/public/scripts/personas.js +++ b/public/scripts/personas.js @@ -111,6 +111,7 @@ export function setUserAvatar(imgfile, { toastPersonaNameChange = true, navigate reloadUserAvatar(); updatePersonaUIStates({ navigateToCurrent: navigateToCurrent }); selectCurrentPersona({ toastPersonaNameChange: toastPersonaNameChange }); + retriggerFirstMessageOnEmptyChat(); saveSettingsDebounced(); $('.zoomed_avatar[forchar]').remove(); } @@ -1782,7 +1783,6 @@ function setNameCallback({ mode = 'all' }, name) { if (!persona) persona = Object.entries(power_user.personas).find(([_, personaName]) => personaName.toLowerCase() === name.toLowerCase())?.[1]; if (persona) { autoSelectPersona(persona); - retriggerFirstMessageOnEmptyChat(); return ''; } else if (mode === 'lookup') { toastr.warning(`Persona ${name} not found`); @@ -1793,7 +1793,6 @@ function setNameCallback({ mode = 'all' }, name) { if (['temp', 'all'].includes(mode)) { // Otherwise, set just the name setUserName(name); //this prevented quickReply usage - retriggerFirstMessageOnEmptyChat(); } return ''; @@ -1944,9 +1943,6 @@ export async function initPersonas() { $(document).on('click', '#user_avatar_block .avatar-container', function () { const imgfile = $(this).attr('data-avatar-id'); setUserAvatar(imgfile); - - // force firstMes {{user}} update on persona switch - retriggerFirstMessageOnEmptyChat(); }); $('#persona_rename_button').on('click', () => renamePersona(user_avatar)); From 97040a98a08d6814d27709320b8ac7cf20aa8ba9 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 28 Mar 2025 01:58:22 +0100 Subject: [PATCH 21/22] Remove 'In Progress' label when marking issues as done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automatically removes the '🧑‍💻 In Progress' label while adding completion labels to keep issue tracking clean and accurate after merging. Updates log messages to reflect both label additions and removals consistently across workflows --- .github/workflows/issues-updates-on-merge.yml | 6 +++--- .github/workflows/pr-auto-manager.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/issues-updates-on-merge.yml b/.github/workflows/issues-updates-on-merge.yml index 3efc9b37f..ddd76b459 100644 --- a/.github/workflows/issues-updates-on-merge.yml +++ b/.github/workflows/issues-updates-on-merge.yml @@ -36,10 +36,10 @@ jobs: for ISSUE in $(echo $issues | jq -r '.[]'); do if [ "${{ github.ref }}" == "refs/heads/staging" ]; then LABEL="✅ Done (staging)" - gh issue edit $ISSUE -R ${{ github.repository }} --add-label "$LABEL" + gh issue edit $ISSUE -R ${{ github.repository }} --add-label "$LABEL" --remove-label "🧑‍💻 In Progress" elif [ "${{ github.ref }}" == "refs/heads/release" ]; then LABEL="✅ Done" - gh issue edit $ISSUE -R ${{ github.repository }} --add-label "$LABEL" + gh issue edit $ISSUE -R ${{ github.repository }} --add-label "$LABEL" --remove-label "🧑‍💻 In Progress" fi - echo "Added label '$LABEL' to issue #$ISSUE" + echo "Added label '$LABEL' (and removed '🧑‍💻 In Progress' if present) in issue #$ISSUE" done diff --git a/.github/workflows/pr-auto-manager.yml b/.github/workflows/pr-auto-manager.yml index cf247ff9d..005fa8da6 100644 --- a/.github/workflows/pr-auto-manager.yml +++ b/.github/workflows/pr-auto-manager.yml @@ -262,6 +262,6 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | for ISSUE in $(echo $final_issues | jq -r '.[]'); do - gh issue edit $ISSUE -R ${{ github.repository }} --add-label "✅ Done (staging)" - echo "Added label '✅ Done (staging)' to issue #$ISSUE" + gh issue edit $ISSUE -R ${{ github.repository }} --add-label "✅ Done (staging)" --remove-label "🧑‍💻 In Progress" + echo "Added label '✅ Done (staging)' (and removed '🧑‍💻 In Progress' if present) in issue #$ISSUE" done From 1323ac1d13b3d811481f4be23d878ef67dd5264a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Sat, 29 Mar 2025 16:05:31 +0200 Subject: [PATCH 22/22] Remove prompt manager export popup Fixes #3782 --- public/scripts/PromptManager.js | 32 ++----------------- .../promptManagerExportForCharacter.html | 4 --- .../templates/promptManagerExportPopup.html | 12 ------- 3 files changed, 2 insertions(+), 46 deletions(-) delete mode 100644 public/scripts/templates/promptManagerExportForCharacter.html delete mode 100644 public/scripts/templates/promptManagerExportPopup.html diff --git a/public/scripts/PromptManager.js b/public/scripts/PromptManager.js index 6e515baa1..94d642afa 100644 --- a/public/scripts/PromptManager.js +++ b/public/scripts/PromptManager.js @@ -1,6 +1,6 @@ 'use strict'; -import { DOMPurify, Popper } from '../lib.js'; +import { DOMPurify } from '../lib.js'; import { event_types, eventSource, is_send_press, main_api, substituteParams } from '../script.js'; import { is_group_generating } from './group-chats.js'; @@ -1440,36 +1440,8 @@ class PromptManager { footerDiv.querySelector('select').selectedIndex = selectedPromptIndex; // Add prompt export dialogue and options - - const exportForCharacter = await renderTemplateAsync('promptManagerExportForCharacter'); - const exportPopup = await renderTemplateAsync('promptManagerExportPopup', { isGlobalStrategy: 'global' === this.configuration.promptOrder.strategy, exportForCharacter }); - rangeBlockDiv.insertAdjacentHTML('beforeend', exportPopup); - - // Destroy previous popper instance if it exists - if (this.exportPopper) { - this.exportPopper.destroy(); - } - - this.exportPopper = Popper.createPopper( - document.getElementById('prompt-manager-export'), - document.getElementById('prompt-manager-export-format-popup'), - { placement: 'bottom' }, - ); - - const showExportSelection = () => { - const popup = document.getElementById('prompt-manager-export-format-popup'); - const show = popup.hasAttribute('data-show'); - - if (show) popup.removeAttribute('data-show'); - else popup.setAttribute('data-show', ''); - - this.exportPopper.update(); - }; - footerDiv.querySelector('#prompt-manager-import').addEventListener('click', this.handleImport); - footerDiv.querySelector('#prompt-manager-export').addEventListener('click', showExportSelection); - rangeBlockDiv.querySelector('.export-promptmanager-prompts-full').addEventListener('click', this.handleFullExport); - rangeBlockDiv.querySelector('.export-promptmanager-prompts-character')?.addEventListener('click', this.handleCharacterExport); + footerDiv.querySelector('#prompt-manager-export').addEventListener('click', this.handleFullExport); } } diff --git a/public/scripts/templates/promptManagerExportForCharacter.html b/public/scripts/templates/promptManagerExportForCharacter.html deleted file mode 100644 index 2127f7dfb..000000000 --- a/public/scripts/templates/promptManagerExportForCharacter.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/public/scripts/templates/promptManagerExportPopup.html b/public/scripts/templates/promptManagerExportPopup.html deleted file mode 100644 index 0beae5446..000000000 --- a/public/scripts/templates/promptManagerExportPopup.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
-
- Export all - -
- {{#if isGlobalStrategy}} - {{else}} - {{{exportForCharacter}}} - {{/if}} -
-