Add /setentryfield command

This commit is contained in:
Cohee 2023-12-01 21:51:49 +02:00
parent 69659e79e3
commit 07962e0e4e
2 changed files with 102 additions and 26 deletions

View File

@ -572,7 +572,7 @@ export function countOccurrences(string, character) {
* @returns {boolean} True if the string is true, false otherwise.
*/
export function isTrueBoolean(arg) {
return ['on', 'true', '1'].includes(arg);
return ['on', 'true', '1'].includes(arg.trim().toLowerCase());
}
/**
@ -581,7 +581,7 @@ export function isTrueBoolean(arg) {
* @returns {boolean} True if the string is false, false otherwise.
*/
export function isFalseBoolean(arg) {
return ['off', 'false', '0'].includes(arg);
return ['off', 'false', '0'].includes(arg.trim().toLowerCase());
}
/**

View File

@ -1,5 +1,5 @@
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPrompt, MAX_INJECTION_DEPTH, extension_prompt_types, getExtensionPromptByName, saveMetadata, getCurrentChatId } from "../script.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition } from "./utils.js";
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean } from "./utils.js";
import { extension_settings, getContext } from "./extensions.js";
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js";
import { registerSlashCommand } from "./slash-commands.js";
@ -196,6 +196,13 @@ function setWorldInfoSettings(settings, data) {
}
function registerWorldInfoSlashCommands() {
function reloadEditor(file) {
const selectedIndex = world_names.indexOf(file);
if (selectedIndex !== -1) {
$('#world_editor_select').val(selectedIndex).trigger('change');
}
}
async function getEntriesFromFile(file) {
if (!file || !world_names.includes(file)) {
toastr.warning('Valid World Info file name is required');
@ -285,6 +292,12 @@ function registerWorldInfoSlashCommands() {
const entry = entries.find(x => x.uid === uid);
if (!entry) {
toastr.warning('Valid UID is required');
return '';
}
if (newEntryTemplate[field] === undefined) {
toastr.warning('Valid field name is required');
return '';
}
@ -298,7 +311,7 @@ function registerWorldInfoSlashCommands() {
return fieldValue.map(x => substituteParams(x)).join(', ');
}
return substituteParams(fieldValue);
return substituteParams(String(fieldValue));
}
async function createEntryCallback(args, content) {
@ -325,19 +338,64 @@ function registerWorldInfoSlashCommands() {
}
await saveWorldInfo(file, data, true);
const selectedIndex = world_names.indexOf(file);
if (selectedIndex !== -1) {
$('#world_editor_select').val(selectedIndex).trigger('change');
}
reloadEditor(file);
return entry.uid;
}
async function setEntryFieldCallback(args, value) {
const file = resolveVariable(args.file);
const uid = resolveVariable(args.uid);
const field = args.field || 'content';
if (value === undefined) {
toastr.warning('Value is required');
return '';
}
const data = await loadWorldInfoData(file);
if (!data || !("entries" in data)) {
toastr.warning('Valid World Info file name is required');
return '';
}
const entry = data.entries[uid];
if (!entry) {
toastr.warning('Valid UID is required');
return '';
}
if (newEntryTemplate[field] === undefined) {
toastr.warning('Valid field name is required');
return '';
}
if (Array.isArray(entry[field])) {
entry[field] = value.split(',').map(x => x.trim()).filter(x => x);
} else if (typeof entry[field] === 'boolean') {
entry[field] = isTrueBoolean(value);
} else if (typeof entry[field] === 'number') {
entry[field] = Number(value);
} else {
entry[field] = value;
}
if (originalDataKeyMap[field]) {
setOriginalDataValue(data, uid, originalDataKeyMap[field], entry[field]);
}
await saveWorldInfo(file, data, true);
reloadEditor(file);
return '';
}
registerSlashCommand('getchatbook', getChatBookCallback, ['getchatlore', 'getchatwi'], ' get a name of the chat-bound lorebook or create a new one if was unbound, and pass it down the pipe', true, true);
registerSlashCommand('findentry', findBookEntryCallback, ['findlore', 'findwi'], `<span class="monospace">(file=bookName field=field [texts])</span> find a UID of the record from the specified book using the fuzzy match of a field value (default: key) and pass it down the pipe, e.g. <tt>/findentry file=chatLore field=key Shadowfang</tt>`, true, true);
registerSlashCommand('getentryfield', getEntryFieldCallback, ['getlorefield', 'getwifield'], '<span class="monospace">(file=bookName field=field [UID])</span> get a field value (default: content) of the record with the UID from the specified book and pass it down the pipe, e.g. <tt>/getentryfield file=chatLore field=content 123</tt>', true, true);
registerSlashCommand('createentry', createEntryCallback, ['createlore', 'createwi'], '<span class="monospace">(file=bookName key=key [content])</span> create a new record in the specified book with the key and content (both are optional) and pass the UID down the pipe, e.g. <tt>/createentry file=chatLore key=Shadowfang The sword of the king</tt>', true, true);
registerSlashCommand('setentryfield', setEntryFieldCallback, ['setlorefield', 'setwifield'], '<span class="monospace">(file=bookName uid=UID field=field [value])</span> set a field value (default: content) of the record with the UID from the specified book. To set multiple values for key fields, use comma-delimited list as a value, e.g. <tt>/setentryfield file=chatLore uid=123 field=key Shadowfang,sword,weapon</tt>', true, true);
}
// World Info Editor
@ -689,6 +747,23 @@ function displayWorldEntries(name, data, navigation = navigation_option.none) {
//$("#world_popup_entries_list").disableSelection();
}
const originalDataKeyMap = {
'displayIndex': 'extensions.display_index',
'excludeRecursion': 'extensions.exclude_recursion',
'selectiveLogic': 'selectiveLogic',
'comment': 'comment',
'constant': 'constant',
'order': 'insertion_order',
'depth': 'extensions.depth',
'probability': 'extensions.probability',
'position': 'extensions.position',
'content': 'content',
'enabled': 'enabled',
'key': 'keys',
'keysecondary': 'secondary_keys',
'selective': 'selective',
}
function setOriginalDataValue(data, uid, key, value) {
if (data.originalData && Array.isArray(data.originalData.entries)) {
let originalEntry = data.originalData.entries.find(x => x.uid === uid);
@ -1283,23 +1358,24 @@ async function deleteWorldInfoEntry(data, uid) {
delete data.entries[uid];
}
const newEntryTemplate = {
key: [],
keysecondary: [],
comment: "",
content: "",
constant: false,
selective: true,
selectiveLogic: 0,
addMemo: false,
order: 100,
position: 0,
disable: false,
excludeRecursion: false,
probability: 100,
useProbability: true,
};
function createWorldInfoEntry(name, data, fromSlashCommand = false) {
const newEntryTemplate = {
key: [],
keysecondary: [],
comment: "",
content: "",
constant: false,
selective: true,
selectiveLogic: 0,
addMemo: false,
order: 100,
position: 0,
disable: false,
excludeRecursion: false,
probability: 100,
useProbability: true,
};
const newUid = getFreeWorldEntryUid(data);
if (!Number.isInteger(newUid)) {
@ -1307,7 +1383,7 @@ function createWorldInfoEntry(name, data, fromSlashCommand = false) {
return;
}
const newEntry = { uid: newUid, ...newEntryTemplate };
const newEntry = { uid: newUid, ...structuredClone(newEntryTemplate) };
data.entries[newUid] = newEntry;
if (!fromSlashCommand) {