Add character specific locks for WI and LB (#1170)

* World Info: Add character locking for entries

With the addition of depth, world info has become more important than
ever. However, there is a conflict that can occur when using multiple
world info or lorebooks on a character. Add the ability to lock
entries to a specific character (or characters) if needed.

This allows for adequate separation especially when there are character
cards each with their separate lorebooks that build off of a core lorebook.

Signed-off-by: kingbri <bdashore3@proton.me>

* World Info: Add entry-specific character exclusions

This is the opposite of locking an entry to a specific character.
Exclusions allows the entry to get included for everyone except the
specified characters in the list.

Signed-off-by: kingbri <bdashore3@proton.me>

---------

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
Brian Dashore 2023-09-23 15:35:51 -04:00 committed by GitHub
parent d9b2b9f753
commit 5421925d6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 109 additions and 0 deletions

View File

@ -3930,6 +3930,18 @@
</div>
</div>
</div>
<div class="flex-container flexFlowColumn flexNoGap wide100p">
<label for="characterFilter" class="range-block-title justifyLeft">
<small data-i18n="Filter to Character(s)">Filter to Character(s)</small>
</label>
<div class="range-block-range">
<select name="characterFilter" multiple>
<option value="">
<span data-i18n="-- Characters not found --">-- Characters not found --</span>
</option>
</select>
</div>
</div>
<div name="WIEntryBottomControls" class="flex-container flex1 justifySpaceBetween world_entry_form_horizontal">
<div class="flex-container flexFlowColumn flexNoGap wi-enter-footer-text ">
<label class="checkbox flex-container">
@ -3958,6 +3970,12 @@
Non-recursable
</span>
</label>
<label class="checkbox flex-container alignitemscenter">
<input type="checkbox" name="character_exclusion" />
<span data-i18n="Character Exclusion">
Character Exclusion
</span>
</label>
</div>
<!--

View File

@ -449,6 +449,86 @@ function getWorldEntry(name, data, entry) {
.prop("selected", true)
.trigger("input");
// Character filter
const characterFilterLabel = template.find(`label[for="characterFilter"] > small`);
characterFilterLabel.text(!!(entry.characterFilter?.isExclude) ? "Exclude Character(s)" : "Filter to Character(s)");
// exclude characters checkbox
const characterExclusionInput = template.find(`input[name="character_exclusion"]`);
characterExclusionInput.data("uid", entry.uid);
characterExclusionInput.on("input", function () {
const uid = $(this).data("uid");
const value = $(this).prop("checked");
characterFilterLabel.text(value ? "Exclude Character(s)" : "Filter to Character(s)");
if (data.entries[uid].characterFilter) {
if (!value && data.entries[uid].characterFilter.names.length === 0) {
delete data.entries[uid].characterFilter;
} else {
data.entries[uid].characterFilter.isExclude = value
}
} else if (value) {
Object.assign(
data.entries[uid],
{
characterFilter: {
isExclude: true,
names: []
}
}
);
}
setOriginalDataValue(data, uid, "character_filter", data.entries[uid].characterFilter);
saveWorldInfo(name, data);
});
characterExclusionInput.prop("checked", entry.characterFilter?.isExclude ?? false).trigger("input");
const characterFilter = template.find(`select[name="characterFilter"]`);
characterFilter.data("uid", entry.uid)
const deviceInfo = getDeviceInfo();
if (deviceInfo && deviceInfo.device.type === 'desktop') {
$(characterFilter).select2({
width: '100%',
placeholder: 'All characters will pull from this entry.',
allowClear: true,
closeOnSelect: false,
});
}
const characters = getContext().characters;
characters.forEach((character) => {
const option = document.createElement('option');
const name = character.avatar.replace(/\.[^/.]+$/, "") ?? character.name
option.innerText = name
option.selected = entry.characterFilter?.names.includes(name)
characterFilter.append(option)
});
characterFilter.on('mousedown change', async function (e) {
// If there's no world names, don't do anything
if (world_names.length === 0) {
e.preventDefault();
return;
}
const uid = $(this).data("uid");
const value = $(this).val();
if ((!value || value?.length === 0) && !data.entries[uid].characterFilter?.isExclude) {
delete data.entries[uid].characterFilter;
} else {
Object.assign(
data.entries[uid],
{
characterFilter: {
isExclude: data.entries[uid].characterFilter?.isExclude ?? false,
names: value
}
}
);
}
setOriginalDataValue(data, uid, "character_filter", data.entries[uid].characterFilter);
saveWorldInfo(name, data);
});
// keysecondary
const keySecondaryInput = template.find('textarea[name="keysecondary"]');
keySecondaryInput.data("uid", entry.uid);
@ -689,6 +769,7 @@ function getWorldEntry(name, data, entry) {
});
disableInput.prop("checked", entry.disable).trigger("input");
// exclude recursion
const excludeRecursionInput = template.find('input[name="exclude_recursion"]');
excludeRecursionInput.data("uid", entry.uid);
excludeRecursionInput.on("input", function () {
@ -1020,6 +1101,16 @@ async function checkWorldInfo(chat, maxContext) {
let activatedNow = new Set();
for (let entry of sortedEntries) {
// Check if this entry applies to the character or if it's excluded
if (entry.characterFilter && entry.characterFilter?.names.length > 0) {
const nameIncluded = entry.characterFilter.names.includes(getCharaFilename());
const filtered = entry.characterFilter.isExclude ? nameIncluded : !nameIncluded
if (filtered) {
continue;
}
}
if (failedProbabilityChecks.has(entry)) {
continue;
}