mirror of
				https://github.com/SillyTavern/SillyTavern.git
				synced 2025-06-05 21:59:27 +02:00 
			
		
		
		
	Merge pull request #496 from bdashore3/dev
Character author's notes + WI individual recursion disabling
This commit is contained in:
		@@ -50,6 +50,7 @@ const extension_settings = {
 | 
			
		||||
    memory: {},
 | 
			
		||||
    note: {
 | 
			
		||||
        default: '',
 | 
			
		||||
        chara: [],
 | 
			
		||||
    },
 | 
			
		||||
    caption: {},
 | 
			
		||||
    expressions: {},
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import { callPopup, eventSource, event_types, getRequestHeaders, saveSettingsDeb
 | 
			
		||||
import { dragElement, isMobile } from "../../RossAscends-mods.js";
 | 
			
		||||
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch } from "../../extensions.js";
 | 
			
		||||
import { power_user } from "../../power-user.js";
 | 
			
		||||
import { onlyUnique, debounce } from "../../utils.js";
 | 
			
		||||
import { onlyUnique, debounce, getCharaFilename } from "../../utils.js";
 | 
			
		||||
export { MODULE_NAME };
 | 
			
		||||
 | 
			
		||||
const MODULE_NAME = 'expressions';
 | 
			
		||||
@@ -437,7 +437,7 @@ function getSpriteFolderName(message) {
 | 
			
		||||
        avatarPath = message.original_avatar || context.characters.find(x => message.force_avatar && message.force_avatar.includes(encodeURIComponent(x.avatar)))?.avatar;
 | 
			
		||||
    }
 | 
			
		||||
    else if (context.characterId) {
 | 
			
		||||
        avatarPath = context.characters[context.characterId].avatar;
 | 
			
		||||
        avatarPath = getCharaFilename();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!avatarPath) {
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import {
 | 
			
		||||
import { selected_group } from "../../group-chats.js";
 | 
			
		||||
import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js";
 | 
			
		||||
import { registerSlashCommand } from "../../slash-commands.js";
 | 
			
		||||
import { getCharaFilename } from "../../utils.js";
 | 
			
		||||
export { MODULE_NAME };
 | 
			
		||||
 | 
			
		||||
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
 | 
			
		||||
@@ -73,7 +74,7 @@ function setNotePositionCommand(_, text) {
 | 
			
		||||
 | 
			
		||||
async function onExtensionFloatingPromptInput() {
 | 
			
		||||
    chat_metadata[metadata_keys.prompt] = $(this).val();
 | 
			
		||||
    $('#extension_floating_prompt_token_counter').text(getTokenCount(chat_metadata[metadata_keys.prompt]));
 | 
			
		||||
    debounce(() => $('#extension_floating_prompt_token_counter').text(getTokenCount(chat_metadata[metadata_keys.prompt])), 1000);
 | 
			
		||||
    saveMetadataDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -99,9 +100,66 @@ async function onExtensionFloatingPositionInput(e) {
 | 
			
		||||
    saveMetadataDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onExtensionFloatingCharaPromptInput() {
 | 
			
		||||
    const tempPrompt = $(this).val();
 | 
			
		||||
    const avatarName = getCharaFilename();
 | 
			
		||||
    let tempCharaNote = {
 | 
			
		||||
        name: avatarName,
 | 
			
		||||
        prompt: tempPrompt
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    debounce(() => $('#extension_floating_chara_token_counter').text(getTokenCount(tempPrompt)), 1000);
 | 
			
		||||
 | 
			
		||||
    let existingCharaNoteIndex;
 | 
			
		||||
    let existingCharaNote;
 | 
			
		||||
 | 
			
		||||
    if (extension_settings.note.chara) {
 | 
			
		||||
        existingCharaNoteIndex = extension_settings.note.chara.findIndex((e) => e.name === avatarName);
 | 
			
		||||
        existingCharaNote = extension_settings.note.chara[existingCharaNoteIndex]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tempPrompt.length === 0 && 
 | 
			
		||||
        extension_settings.note.chara && 
 | 
			
		||||
        existingCharaNote &&
 | 
			
		||||
        !existingCharaNote.useChara
 | 
			
		||||
    ) {
 | 
			
		||||
        extension_settings.note.chara.splice(existingCharaNoteIndex, 1);
 | 
			
		||||
    }
 | 
			
		||||
    else if (extension_settings.note.chara && existingCharaNote) {
 | 
			
		||||
        Object.assign(existingCharaNote, tempCharaNote);
 | 
			
		||||
    } 
 | 
			
		||||
    else if (avatarName && tempPrompt.length > 0) {
 | 
			
		||||
        if (!extension_settings.note.chara) {
 | 
			
		||||
            extension_settings.note.chara = []
 | 
			
		||||
        }
 | 
			
		||||
        Object.assign(tempCharaNote, { useChara: false })
 | 
			
		||||
 | 
			
		||||
        extension_settings.note.chara.push(tempCharaNote);
 | 
			
		||||
    } else {
 | 
			
		||||
        console.log("Character author's note error: No avatar name key could be found.");
 | 
			
		||||
        toastr.error("Something went wrong. Could not save character's author's note.");
 | 
			
		||||
 | 
			
		||||
        // Don't save settings if something went wrong
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onExtensionFloatingCharaCheckboxChanged() {
 | 
			
		||||
    const value = !!$(this).prop('checked');
 | 
			
		||||
    const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
 | 
			
		||||
 | 
			
		||||
    if (charaNote) {
 | 
			
		||||
        charaNote.useChara = value;
 | 
			
		||||
 | 
			
		||||
        saveSettingsDebounced();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onExtensionFloatingDefaultInput() {
 | 
			
		||||
    extension_settings.note.default = $(this).val();
 | 
			
		||||
    $('#extension_floating_default_token_counter').text(getTokenCount(extension_settings.note.default));
 | 
			
		||||
    debounce(() => $('#extension_floating_default_token_counter').text(getTokenCount(extension_settings.note.default)), 1000);
 | 
			
		||||
    saveSettingsDebounced();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -114,6 +172,14 @@ function loadSettings() {
 | 
			
		||||
    $('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]);
 | 
			
		||||
    $('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]);
 | 
			
		||||
    $(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true);
 | 
			
		||||
 | 
			
		||||
    if (extension_settings.note.chara) {
 | 
			
		||||
        const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
 | 
			
		||||
 | 
			
		||||
        $('#extension_floating_chara').val(charaNote ? charaNote.prompt : '');
 | 
			
		||||
        $('#extension_use_floating_chara').prop('checked', charaNote ? charaNote.useChara : false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $('#extension_floating_default').val(extension_settings.note.default);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -144,7 +210,17 @@ async function moduleWorker() {
 | 
			
		||||
        ? (lastMessageNumber % chat_metadata[metadata_keys.interval])
 | 
			
		||||
        : (chat_metadata[metadata_keys.interval] - lastMessageNumber);
 | 
			
		||||
    const shouldAddPrompt = messagesTillInsertion == 0;
 | 
			
		||||
    const prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : '';
 | 
			
		||||
 | 
			
		||||
    let prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : '';
 | 
			
		||||
    if (shouldAddPrompt && extension_settings.note.chara) {
 | 
			
		||||
        const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
 | 
			
		||||
 | 
			
		||||
        // Only replace with the chara note if the user checked the box
 | 
			
		||||
        if (charaNote && charaNote.useChara) {
 | 
			
		||||
            prompt = charaNote.prompt;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
 | 
			
		||||
    $('#extension_floating_counter').text(shouldAddPrompt ? '0' : messagesTillInsertion);
 | 
			
		||||
}
 | 
			
		||||
@@ -184,9 +260,23 @@ function onANMenuItemClick() {
 | 
			
		||||
 | 
			
		||||
function onChatChanged() {
 | 
			
		||||
    const tokenCounter1 = chat_metadata[metadata_keys.prompt] ? getTokenCount(chat_metadata[metadata_keys.prompt]) : 0;
 | 
			
		||||
    const tokenCounter2 = extension_settings.note.default ? getTokenCount(extension_settings.note.default) : 0;
 | 
			
		||||
    $('#extension_floating_prompt_token_counter').text(tokenCounter1);
 | 
			
		||||
    $('#extension_floating_default_token_counter').text(tokenCounter2);
 | 
			
		||||
 | 
			
		||||
    let tokenCounter2;
 | 
			
		||||
    if (extension_settings.note.chara) {
 | 
			
		||||
        const charaNote = extension_settings.note.chara.find((e) => e.name === getCharaFilename());
 | 
			
		||||
 | 
			
		||||
        if (charaNote) {
 | 
			
		||||
            tokenCounter2 = getTokenCount(charaNote.prompt);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tokenCounter2) {
 | 
			
		||||
        $('#extension_floating_chara_token_counter').text(tokenCounter2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tokenCounter3 = extension_settings.note.default ? getTokenCount(extension_settings.note.default) : 0;
 | 
			
		||||
    $('#extension_floating_default_token_counter').text(tokenCounter3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
(function () {
 | 
			
		||||
@@ -235,6 +325,25 @@ function onChatChanged() {
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <hr class="sysHR">
 | 
			
		||||
                <div class="inline-drawer">
 | 
			
		||||
                    <div id="charaANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
                        <b>Character Author's Note</b>
 | 
			
		||||
                        <div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="inline-drawer-content">
 | 
			
		||||
                    <small>Will be automatically added as the author's note for this character.</small>
 | 
			
		||||
 | 
			
		||||
                        <textarea id="extension_floating_chara" class="text_pole" rows="8" maxlength="10000"
 | 
			
		||||
                        placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
 | 
			
		||||
                        <div class="extension_token_counter">Tokens: <span id="extension_floating_chara_token_counter">0</small></div>
 | 
			
		||||
 | 
			
		||||
                        <label for="extension_use_floating_chara">
 | 
			
		||||
                            <input id="extension_use_floating_chara" type="checkbox" />
 | 
			
		||||
                        <span data-i18n="Use character author's note">Use character author's note</span>
 | 
			
		||||
                    </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <hr class="sysHR">
 | 
			
		||||
                <div class="inline-drawer">
 | 
			
		||||
                    <div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
 | 
			
		||||
                        <b>Default Author's Note</b>
 | 
			
		||||
@@ -263,6 +372,8 @@ function onChatChanged() {
 | 
			
		||||
        $('#extension_floating_prompt').on('input', onExtensionFloatingPromptInput);
 | 
			
		||||
        $('#extension_floating_interval').on('input', onExtensionFloatingIntervalInput);
 | 
			
		||||
        $('#extension_floating_depth').on('input', onExtensionFloatingDepthInput);
 | 
			
		||||
        $('#extension_floating_chara').on('input', onExtensionFloatingCharaPromptInput);
 | 
			
		||||
        $('#extension_use_floating_chara').on('input', onExtensionFloatingCharaCheckboxChanged);
 | 
			
		||||
        $('#extension_floating_default').on('input', onExtensionFloatingDefaultInput);
 | 
			
		||||
        $('input[name="extension_floating_position"]').on('change', onExtensionFloatingPositionInput);
 | 
			
		||||
        $('#ANClose').on('click', function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
import { getContext } from "./extensions.js";
 | 
			
		||||
 | 
			
		||||
export function onlyUnique(value, index, array) {
 | 
			
		||||
    return array.indexOf(value) === index;
 | 
			
		||||
}
 | 
			
		||||
@@ -420,3 +422,12 @@ export function isDataURL(str) {
 | 
			
		||||
    const regex = /^data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)*;?)?(base64)?,([a-z0-9!$&',()*+;=\-_%.~:@\/?#]+)?$/i;
 | 
			
		||||
    return regex.test(str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getCharaFilename() {
 | 
			
		||||
    const context = getContext();
 | 
			
		||||
    const fileName = context.characters[context.characterId].avatar;
 | 
			
		||||
 | 
			
		||||
    if (fileName) {
 | 
			
		||||
        return fileName.replace(/\.[^/.]+$/, "")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -332,6 +332,19 @@ function appendWorldEntry(entry) {
 | 
			
		||||
        $(this).siblings("input").click();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const excludeRecursionInput = template.find('input[name="exclude_recursion"]');
 | 
			
		||||
    excludeRecursionInput.data("uid", entry.uid);
 | 
			
		||||
    excludeRecursionInput.on("input", function () {
 | 
			
		||||
        const uid = $(this).data("uid");
 | 
			
		||||
        const value = $(this).prop("checked");
 | 
			
		||||
        world_info_data.entries[uid].excludeRecursion = value;
 | 
			
		||||
        saveWorldInfo();
 | 
			
		||||
    });
 | 
			
		||||
    excludeRecursionInput.prop("checked", entry.excludeRecursion).trigger("input");
 | 
			
		||||
    excludeRecursionInput.siblings(".checkbox_fancy").click(function () {
 | 
			
		||||
        $(this).siblings("input").click();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // delete button
 | 
			
		||||
    const deleteButton = template.find("input.delete_entry_button");
 | 
			
		||||
    deleteButton.data("uid", entry.uid);
 | 
			
		||||
@@ -366,6 +379,7 @@ function createWorldInfoEntry() {
 | 
			
		||||
        order: 100,
 | 
			
		||||
        position: 0,
 | 
			
		||||
        disable: false,
 | 
			
		||||
        excludeRecursion: false
 | 
			
		||||
    };
 | 
			
		||||
    const newUid = getFreeWorldEntryUid();
 | 
			
		||||
 | 
			
		||||
@@ -505,6 +519,7 @@ function checkWorldInfo(chat) {
 | 
			
		||||
    let worldInfoBefore = "";
 | 
			
		||||
    let worldInfoAfter = "";
 | 
			
		||||
    let needsToScan = true;
 | 
			
		||||
    let count = 0;
 | 
			
		||||
    let allActivatedEntries = new Set();
 | 
			
		||||
 | 
			
		||||
    const sortedEntries = Object.keys(world_info_data.entries)
 | 
			
		||||
@@ -512,10 +527,13 @@ function checkWorldInfo(chat) {
 | 
			
		||||
        .sort((a, b) => b.order - a.order);
 | 
			
		||||
 | 
			
		||||
    while (needsToScan) {
 | 
			
		||||
        // Track how many times the loop has run
 | 
			
		||||
        count++;
 | 
			
		||||
 | 
			
		||||
        let activatedNow = new Set();
 | 
			
		||||
 | 
			
		||||
        for (let entry of sortedEntries) {
 | 
			
		||||
            if (allActivatedEntries.has(entry.uid) || entry.disable == true) {
 | 
			
		||||
            if (allActivatedEntries.has(entry.uid) || entry.disable == true || (count > 1 && world_info_recursive && entry.excludeRecursion)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user