WI: Split entry header and editor

9a82b688baf495ef42af5afbf4f2675dda5327f2

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

3 files changed, +365 -327Showing whitespace changes
public/css/world-info.css+4 -0
@@ -201,6 +201,10 @@
201201 transition: opacity 500ms;
202202}
203203
204+.world_entry_edit {
205+ display: contents;
206+}
207+
204208.disabledWIEntry {
205209 opacity: 0.4;
206210 filter: grayscale(1);
public/index.html+76 -73
@@ -6193,79 +6193,7 @@
61936193 </div>
61946194 </div>
61956195 <div id="entry_edit_template" class="template_element">
61966196 <div class="world_entryworld_entry_edit">
6197- <form class="world_entry_form wi-card-entry">
6198- <div class="inline-drawer wide100p">
6199- <div class="inline-drawer-header gap5px padding0">
6200- <span class="drag-handle">&#9776;</span>
6201- <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">
6202- <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
6203- <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" data-i18n="[title]Toggle entry's active state." title="Toggle entry's active state."></div>
6204- <div class="flex-container alignitemscenter wide100p">
6205- <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">
6206- <div class="flex-container flex1">
6207- <textarea class="text_pole" rows="1" name="comment" data-i18n="[placeholder]Entry Title/Memo" placeholder="Entry Title/Memo"></textarea>
6208- </div>
6209- <!-- <span class="world_entry_form_position_value"></span> -->
6210- <select data-i18n="[title]WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized" title="WI Entry Status:&#13;🔵 Constant&#13;🟢 Normal&#13;🔗 Vectorized" name="entryStateSelector" class="text_pole widthNatural margin0">
6211- <option value="constant" title="Constant" data-i18n="[title]WI_Entry_Status_Constant">🔵</option>
6212- <option value="normal" title="Normal" data-i18n="[title]WI_Entry_Status_Normal">🟢</option>
6213- <option value="vectorized" title="Vectorized" data-i18n="[title]WI_Entry_Status_Vectorized">🔗</option>
6214- </select>
6215- </div>
6216- <div class="WIEnteryHeaderControls flex-container">
6217- <div name="PositionBlock" class="world_entry_form_control world_entry_form_radios wi-enter-footer-text">
6218- <label for="position" class="WIEntryHeaderTitleMobile" data-i18n="Position:">Position:</label>
6219- <select name="position" class="text_pole widthNatural margin0" data-i18n="[title]T_Position" title="↑Char: Before Character Definitions&#13;↓Char: After Character Definitions&#13;↑EM: Before Example Messages&#13;↓EM: After Example Messages&#13;↑AN: Before Author's Note&#13;↓AN: After Author's Note&#13;@D ⚙️: at Depth (System)&#13;@D 👤: at Depth (User)&#13;@D 🤖: at Depth (Assistant)">
6220- <option value="0" data-role="" data-i18n="Before Char Defs">
6221- ↑Char
6222- </option>
6223- <option value="1" data-role="" data-i18n="After Char Defs">
6224- ↓Char
6225- </option>
6226- <option value="5" data-role="" data-i18n="Before EM">
6227- ↑EM
6228- </option>
6229- <option value="6" data-role="" data-i18n="After EM">
6230- ↓EM
6231- </option>
6232- <option value="2" data-role="" data-i18n="Before AN">
6233- ↑AN
6234- </option>
6235- <option value="3" data-role="" data-i18n="After AN">
6236- ↓AN
6237- </option>
6238- <option value="4" data-role="0" data-i18n="at Depth System">
6239- @D ⚙️
6240- </option>
6241- <option value="4" data-role="1" data-i18n="at Depth User">
6242- @D 👤
6243- </option>
6244- <option value="4" data-role="2" data-i18n="at Depth AI">
6245- @D 🤖
6246- </option>
6247- </select>
6248- </div>
6249- <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6250- <label for="depth" class="WIEntryHeaderTitleMobile" data-i18n="Depth:">Depth:</label>
6251- <input title="Depth" class="text_pole wideMax100px margin0" type="number" name="depth" data-i18n="[title]Depth" placeholder="" min="0" max="9999" />
6252- </div>
6253- <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6254- <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Order:">Order:</label>
6255- <input title="Order" data-i18n="[title]Order" class="text_pole wideMax100px margin0" type="number" name="order" placeholder="" min="0" max="9999" />
6256- </div>
6257- <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap probabilityContainer">
6258- <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Trigger %:">Trigger %:</label>
6259- <input title="Probability" data-i18n="[title]Probability" class="text_pole wideMax100px margin0" type="number" name="probability" placeholder="" min="0" max="100" />
6260- </div>
6261- </div>
6262- </div>
6263- </div>
6264- <i class="menu_button move_entry_button fa-solid fa-right-left" title="Move Entry to Another Lorebook" data-i18n="[title]Move Entry to Another Lorebook"></i>
6265- <i class="menu_button duplicate_entry_button fa-solid fa-paste" title="Duplicate world info entry" data-i18n="[title]Duplicate world info entry" type="submit" value=""></i>
6266- <i class="menu_button delete_entry_button fa-solid fa-trash-can" title="Delete world info entry" data-i18n="[title]Delete world info entry" type="submit" value=""></i>
6267- </div>
6268- <div class="inline-drawer-content flex-container paddingBottom5px wide100p">
62696197 <div class="flex-container wide100p alignitemscenter">
62706198 <div name="keywordsAndLogicBlock" class="flex-container wide100p alignitemscenter">
62716199 <div class="world_entry_form_control keyprimary flex1">
@@ -6557,6 +6485,81 @@
65576485 </div>
65586486 </div>
65596487 </div>
6488+ <div class="world_entry">
6489+ <form class="world_entry_form wi-card-entry">
6490+ <div class="inline-drawer wide100p">
6491+ <div class="inline-drawer-header gap5px padding0">
6492+ <span class="drag-handle">&#9776;</span>
6493+ <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">
6494+ <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
6495+ <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" data-i18n="[title]Toggle entry's active state." title="Toggle entry's active state."></div>
6496+ <div class="flex-container alignitemscenter wide100p">
6497+ <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">
6498+ <div class="flex-container flex1">
6499+ <textarea class="text_pole" rows="1" name="comment" data-i18n="[placeholder]Entry Title/Memo" placeholder="Entry Title/Memo"></textarea>
6500+ </div>
6501+ <!-- <span class="world_entry_form_position_value"></span> -->
6502+ <select data-i18n="[title]WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized" title="WI Entry Status:&#13;🔵 Constant&#13;🟢 Normal&#13;🔗 Vectorized" name="entryStateSelector" class="text_pole widthNatural margin0">
6503+ <option value="constant" title="Constant" data-i18n="[title]WI_Entry_Status_Constant">🔵</option>
6504+ <option value="normal" title="Normal" data-i18n="[title]WI_Entry_Status_Normal">🟢</option>
6505+ <option value="vectorized" title="Vectorized" data-i18n="[title]WI_Entry_Status_Vectorized">🔗</option>
6506+ </select>
6507+ </div>
6508+ <div class="WIEnteryHeaderControls flex-container">
6509+ <div name="PositionBlock" class="world_entry_form_control world_entry_form_radios wi-enter-footer-text">
6510+ <label for="position" class="WIEntryHeaderTitleMobile" data-i18n="Position:">Position:</label>
6511+ <select name="position" class="text_pole widthNatural margin0" data-i18n="[title]T_Position" title="↑Char: Before Character Definitions&#13;↓Char: After Character Definitions&#13;↑EM: Before Example Messages&#13;↓EM: After Example Messages&#13;↑AN: Before Author's Note&#13;↓AN: After Author's Note&#13;@D ⚙️: at Depth (System)&#13;@D 👤: at Depth (User)&#13;@D 🤖: at Depth (Assistant)">
6512+ <option value="0" data-role="" data-i18n="Before Char Defs">
6513+ ↑Char
6514+ </option>
6515+ <option value="1" data-role="" data-i18n="After Char Defs">
6516+ ↓Char
6517+ </option>
6518+ <option value="5" data-role="" data-i18n="Before EM">
6519+ ↑EM
6520+ </option>
6521+ <option value="6" data-role="" data-i18n="After EM">
6522+ ↓EM
6523+ </option>
6524+ <option value="2" data-role="" data-i18n="Before AN">
6525+ ↑AN
6526+ </option>
6527+ <option value="3" data-role="" data-i18n="After AN">
6528+ ↓AN
6529+ </option>
6530+ <option value="4" data-role="0" data-i18n="at Depth System">
6531+ @D ⚙️
6532+ </option>
6533+ <option value="4" data-role="1" data-i18n="at Depth User">
6534+ @D 👤
6535+ </option>
6536+ <option value="4" data-role="2" data-i18n="at Depth AI">
6537+ @D 🤖
6538+ </option>
6539+ </select>
6540+ </div>
6541+ <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6542+ <label for="depth" class="WIEntryHeaderTitleMobile" data-i18n="Depth:">Depth:</label>
6543+ <input title="Depth" class="text_pole wideMax100px margin0" type="number" name="depth" data-i18n="[title]Depth" placeholder="" min="0" max="9999" />
6544+ </div>
6545+ <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6546+ <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Order:">Order:</label>
6547+ <input title="Order" data-i18n="[title]Order" class="text_pole wideMax100px margin0" type="number" name="order" placeholder="" min="0" max="9999" />
6548+ </div>
6549+ <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap probabilityContainer">
6550+ <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Trigger %:">Trigger %:</label>
6551+ <input title="Probability" data-i18n="[title]Probability" class="text_pole wideMax100px margin0" type="number" name="probability" placeholder="" min="0" max="100" />
6552+ </div>
6553+ </div>
6554+ </div>
6555+ </div>
6556+ <i class="menu_button move_entry_button fa-solid fa-right-left" title="Move Entry to Another Lorebook" data-i18n="[title]Move Entry to Another Lorebook"></i>
6557+ <i class="menu_button duplicate_entry_button fa-solid fa-paste" title="Duplicate world info entry" data-i18n="[title]Duplicate world info entry" type="submit" value=""></i>
6558+ <i class="menu_button delete_entry_button fa-solid fa-trash-can" title="Delete world info entry" data-i18n="[title]Delete world info entry" type="submit" value=""></i>
6559+ </div>
6560+ <div class="inline-drawer-content inline-drawer-outlet flex-container paddingBottom5px wide100p ">
6561+ <!-- Outlet for world_entry_edit -->
6562+ </div>
65606563 </div>
65616564 </form>
65626565 </div>
public/scripts/world-info.js+285 -254
@@ -58,7 +58,8 @@ export const scan_state = {
5858 MIN_ACTIVATIONS: 3,
5959};
6060
6161const WI_ENTRY_EDIT_TEMPLATEWI_ENTRY_HEADER_TEMPLATE = $('#entry_edit_template .world_entry');
62+const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry_edit');
6263
6364export let world_info = {};
6465export let selected_world_info = [];
@@ -1939,9 +1940,8 @@ function updateWorldEntryKeyOptionsCache(keyOptions, { remove = false, reset = f
19391940 worldEntryKeyOptionsCache.sort((a, b) => b.count - a.count || a.text.localeCompare(b.text));
19401941}
19411942
19421943function clearEntryList($list) {
19431944 console.time('clearEntryList');
1944- const $list = $('#world_popup_entries_list');
19451945
19461946 if (!$list.children().length) {
19471947 console.debug('List already empty, skipping cleanup.');
@@ -2007,7 +2007,7 @@ async function displayWorldEntries(name, data, navigation = navigation_option.no
20072007
20082008 worldEntriesList.css({ 'opacity': 0, 'transition': 'opacity 250ms ease-in-out' });
20092009 await delay(250);
20102010 clearEntryList(worldEntriesList);
20112011 worldEntriesList.show();
20122012
20132013 if (!data || !('entries' in data)) {
@@ -2104,7 +2104,7 @@ async function displayWorldEntries(name, data, navigation = navigation_option.no
21042104 try {
21052105 // Prevent saveWorldInfo from firing timeouts while rendering the list
21062106 isSaveWorldInfoDisabled = true;
21072107 clearEntryList(worldEntriesList);
21082108
21092109 const keywordHeaders = await renderTemplateAsync('worldInfoKeywordHeaders');
21102110 const blocks = [];
@@ -2885,82 +2885,14 @@ function handleEntryKillSwitchHelper({ entryKillSwitch, entry, data, name, setWI
28852885export async function getWorldEntry(name, data, entry) {
28862886 if (!data.entries[entry.uid]) return;
28872887
28882888 const templateheaderTemplate = WI_ENTRY_EDIT_TEMPLATEWI_ENTRY_HEADER_TEMPLATE.clone();
28892889 templateheaderTemplate.data('uid', entry.uid);
28902890 templateheaderTemplate.attr('uid', entry.uid);
28912891
28922892 if (typeof power_user.wi_key_input_plaintext === 'undefined') power_user.wi_key_input_plaintext = true;
28932893
2894- // Key inputs
2895- const keyInput = enableKeysInputHelper({ template, entry, entryPropName: 'key', originalDataValueName: 'keys', name, data });
2896- const keySecondaryInput = enableKeysInputHelper({ template, entry, entryPropName: 'keysecondary', originalDataValueName: 'secondary_keys', name, data });
2897-
2898- // Key input switch
2899- template.find('.switch_input_type_icon').on('click', function () {
2900- power_user.wi_key_input_plaintext = !power_user.wi_key_input_plaintext;
2901- saveSettingsDebounced();
2902- const uid = ($(this).parents('.world_entry')).data('uid');
2903- updateEditor(uid, false);
2904- $(`.world_entry[uid="${uid}"] .inline-drawer-icon`).trigger('click');
2905- }).each((_, icon) => {
2906- $(icon).attr('title', $(icon).data(power_user.wi_key_input_plaintext ? 'tooltip-on' : 'tooltip-off'));
2907- $(icon).text($(icon).data(power_user.wi_key_input_plaintext ? 'icon-on' : 'icon-off'));
2908- });
2909-
2910- // Logic AND/NOT
2911- const selectiveLogicDropdown = template.find('select[name="entryLogicType"]');
2912- selectiveLogicDropdown.data('uid', entry.uid);
2913- selectiveLogicDropdown.on('click', e => e.stopPropagation());
2914- selectiveLogicDropdown.on('input', async function () {
2915- const uid = $(this).data('uid');
2916- const value = Number($(this).val());
2917- data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY;
2918- setWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic);
2919- await saveWorldInfo(name, data);
2920- });
2921- template.find(`select[name="entryLogicType"] option[value=${entry.selectiveLogic}]`).prop('selected', true).trigger('input');
2922-
2923- // Character filter
2924- const characterFilterLabel = template.find('label[for="characterFilter"] > small');
2925- characterFilterLabel.text(entry.characterFilter?.isExclude ? 'Exclude Character(s)' : 'Filter to Character(s)');
2926- const characterExclusionInput = template.find('input[name="character_exclusion"]');
2927- characterExclusionInput.data('uid', entry.uid);
2928- characterExclusionInput.on('input', async function () {
2929- const uid = $(this).data('uid');
2930- const value = $(this).prop('checked');
2931- characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)');
2932- if (data.entries[uid].characterFilter) {
2933- if (!value && data.entries[uid].characterFilter.names.length === 0 && data.entries[uid].characterFilter.tags.length === 0) {
2934- delete data.entries[uid].characterFilter;
2935- } else {
2936- data.entries[uid].characterFilter.isExclude = value;
2937- }
2938- } else if (value) {
2939- Object.assign(data.entries[uid], { characterFilter: { isExclude: true, names: [], tags: [] } });
2940- }
2941- if (data.entries[uid]?.characterFilter?.names?.length > 0) {
2942- for (const name of [...data.entries[uid].characterFilter.names]) {
2943- if (!getContext().characters.find(x => x.avatar.replace(/\.[^/.]+$/, '') === name)) {
2944- data.entries[uid].characterFilter.names = data.entries[uid].characterFilter.names.filter(x => x !== name);
2945- }
2946- }
2947- }
2948- setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);
2949- await saveWorldInfo(name, data);
2950- });
2951- characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input');
2952-
2953- const characterFilter = template.find('select[name="characterFilter"]');
2954- characterFilter.data('uid', entry.uid);
2955- initCharacterFilterSelect2Helper(characterFilter, t);
2956- fillCharacterAndTagOptionsHelper({ characterFilter, entry, getContext });
2957- handleCharacterFilterChangeHelper({
2958- characterFilter, data, entry, name, world_names, getContext, setWIOriginalDataValue, saveWorldInfo, t,
2959- });
2960-
29612894 // Comment
29622895 const commentInput = templateheaderTemplate.find('textarea[name="comment"]');
2963- const commentToggle = template.find('input[name="addMemo"]');
29642896 commentInput.data('uid', entry.uid);
29652897 commentInput.on('input', async function (_, { skipReset } = {}) {
29662898 const uid = $(this).data('uid');
@@ -2970,157 +2902,43 @@ export async function getWorldEntry(name, data, entry) {
29702902 setWIOriginalDataValue(data, uid, 'comment', data.entries[uid].comment);
29712903 await saveWorldInfo(name, data);
29722904 });
2973- commentToggle.data('uid', entry.uid);
2974- commentToggle.on('input', async function () {
2975- const uid = $(this).data('uid');
2976- const value = $(this).prop('checked');
2977- const commentContainer = $(this).closest('.world_entry').find('.commentContainer');
2978- data.entries[uid].addMemo = value;
2979- await saveWorldInfo(name, data);
2980- value ? commentContainer.show() : commentContainer.hide();
2981- });
29822905 commentInput.val(entry.comment).trigger('input', { skipReset: true });
2983- commentToggle.prop('checked', true).trigger('input');
2984- commentToggle.parent().hide();
2985-
2986- // Content
2987- const counter = template.find('.world_entry_form_token_counter');
2988- const countTokensDebounced = debounce(async function (counter, value) {
2989- const numberOfTokens = await getTokenCountAsync(value);
2990- $(counter).text(numberOfTokens);
2991- }, debounce_timeout.relaxed);
2992- const contentInputId = `world_entry_content_${entry.uid}`;
2993- const contentInput = template.find('textarea[name="content"]');
2994- contentInput.data('uid', entry.uid);
2995- contentInput.attr('id', contentInputId);
2996- contentInput.on('input', async function (_, { skipCount } = {}) {
2997- const uid = $(this).data('uid');
2998- const value = $(this).val();
2999- data.entries[uid].content = value;
3000- setWIOriginalDataValue(data, uid, 'content', data.entries[uid].content);
3001- await saveWorldInfo(name, data);
3002- if (!skipCount) countTokensDebounced(counter, value);
3003- });
3004- contentInput.val(entry.content).trigger('input', { skipCount: true });
3005- template.find('.editor_maximize').attr('data-for', contentInputId);
3006- template.find('.inline-drawer-toggle').on('click', function () {
3007- if (counter.data('first-run')) {
3008- counter.data('first-run', false);
3009- countTokensDebounced(counter, contentInput.val());
3010- if (!keyInput.isFancy) initScrollHeight(keyInput.control);
3011- if (!keySecondaryInput.isFancy) initScrollHeight(keySecondaryInput.control);
3012- }
3013- });
3014-
3015- // Selective
3016- const selectiveInput = template.find('input[name="selective"]');
3017- selectiveInput.data('uid', entry.uid);
3018- selectiveInput.on('input', async function () {
3019- const uid = $(this).data('uid');
3020- const value = $(this).prop('checked');
3021- data.entries[uid].selective = value;
3022- setWIOriginalDataValue(data, uid, 'selective', data.entries[uid].selective);
3023- await saveWorldInfo(name, data);
3024- const keysecondary = $(this).closest('.world_entry').find('.keysecondary');
3025- const keysecondarytextpole = $(this).closest('.world_entry').find('.keysecondarytextpole');
3026- const keyprimaryselect = $(this).closest('.world_entry').find('.keyprimaryselect');
3027- const keyprimaryHeight = keyprimaryselect.outerHeight();
3028- keysecondarytextpole.css('height', keyprimaryHeight + 'px');
3029- value ? keysecondary.show() : keysecondary.hide();
3030- });
3031- selectiveInput.prop('checked', true).trigger('input');
3032- selectiveInput.parent().hide();
30332906
30342907 // Order
30352908 const orderInput = templateheaderTemplate.find('input[name="order"]');
30362909 orderInput.data('uid', entry.uid);
30372910 orderInput.on('input', async function () {
30382911 const uid = $(this).data('uid');
30392912 const value = Number($(this).val());
30402913 data.entries[uid].order = !isNaN(value) ? value : 0;
30412914 updatePosOrdDisplayHelper({ template: headerTemplate, data, uid });
30422915 setWIOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order);
30432916 await saveWorldInfo(name, data);
30442917 });
30452918 orderInput.val(entry.order).trigger('input');
30462919 orderInput.css('width', 'calc(3em + 15px)');
30472920
3048- // Group
3049- const groupInput = template.find('input[name="group"]');
3050- groupInput.data('uid', entry.uid);
3051- groupInput.on('input', async function () {
3052- const uid = $(this).data('uid');
3053- const value = String($(this).val()).trim();
3054- data.entries[uid].group = value;
3055- setWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group);
3056- await saveWorldInfo(name, data);
3057- });
3058- groupInput.val(entry.group ?? '').trigger('input');
3059- setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1);
3060-
3061- // Inclusion priority
3062- const groupOverrideInput = template.find('input[name="groupOverride"]');
3063- groupOverrideInput.data('uid', entry.uid);
3064- groupOverrideInput.on('input', async function () {
3065- const uid = $(this).data('uid');
3066- const value = $(this).prop('checked');
3067- data.entries[uid].groupOverride = value;
3068- setWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride);
3069- await saveWorldInfo(name, data);
3070- });
3071- groupOverrideInput.prop('checked', entry.groupOverride).trigger('input');
3072-
3073- // Group weight
3074- handleNumberInputHelper({
3075- inputElem: template.find('input[name="groupWeight"]'),
3076- entry, entryKey: 'groupWeight', data, name, setWIOriginalDataValue, saveWorldInfo,
3077- min: 1, max: 10000, clamp: true,
3078- });
3079-
3080- // Sticky, cooldown, delay
3081- handleNumberInputHelper({
3082- inputElem: template.find('input[name="sticky"]'),
3083- entry, entryKey: 'sticky', data, name, setWIOriginalDataValue, saveWorldInfo,
3084- min: 1, max: 10000, clamp: false,
3085- });
3086- handleNumberInputHelper({
3087- inputElem: template.find('input[name="cooldown"]'),
3088- entry, entryKey: 'cooldown', data, name, setWIOriginalDataValue, saveWorldInfo,
3089- min: 1, max: 10000, clamp: false,
3090- });
3091- handleNumberInputHelper({
3092- inputElem: template.find('input[name="delay"]'),
3093- entry, entryKey: 'delay', data, name, setWIOriginalDataValue, saveWorldInfo,
3094- min: 1, max: 10000, clamp: false,
3095- });
3096-
30972921 // Probability
30982922 handleProbabilityInputHelper({ probabilityInput: templateheaderTemplate.find('input[name="probability"]'), data, entry, name, setWIOriginalDataValue, saveWorldInfo });
3099- handleProbabilityToggleHelper({
3100- probabilityToggle: template.find('input[name="useProbability"]'),
3101- data, entry, name,
3102- probabilityInput: template.find('input[name="probability"]'),
3103- setWIOriginalDataValue, saveWorldInfo,
3104- });
31052923
31062924 // Depth
31072925 handleNumberInputHelper({
31082926 inputElem: templateheaderTemplate.find('input[name="depth"]'),
31092927 entry, entryKey: 'depth', data, name, setWIOriginalDataValue, saveWorldInfo,
31102928 min: 0, max: MAX_SCAN_DEPTH, clamp: false,
31112929 });
31122930 templateheaderTemplate.find('input[name="depth"]').css('width', 'calc(3em + 15px)');
31132931
31142932 // Position
31152933 if (entry.position === undefined) entry.position = 0;
31162934 const positionInput = templateheaderTemplate.find('select[name="position"]');
31172935 positionInput.data('uid', entry.uid);
31182936 positionInput.on('click', e => e.stopPropagation());
31192937 positionInput.on('input', async function () {
31202938 const uid = $(this).data('uid');
31212939 const value = Number($(this).val());
31222940 data.entries[uid].position = !isNaN(value) ? value : 0;
31232941 const depthInput = templateheaderTemplate.find('input[name="depth"]');
31242942 if (value === world_info_position.atDepth) {
31252943 depthInput.prop('disabled', false);
31262944 depthInput.css('visibility', 'visible');
@@ -3131,64 +2949,29 @@ export async function getWorldEntry(name, data, entry) {
31312949 depthInput.css('visibility', 'hidden');
31322950 data.entries[uid].role = null;
31332951 }
31342952 updatePosOrdDisplayHelper({ template: headerTemplate, data, uid });
31352953 setWIOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char');
31362954 setWIOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position);
31372955 setWIOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role);
31382956 await saveWorldInfo(name, data);
31392957 });
31402958 const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : '';
31412959 templateheaderTemplate.find(`select[name="position"] option[value="${entry.position}"][data-role="${roleValue}"]`).prop('selected', true).trigger('input');
3142-
3143- // UID display
3144- template.find('.world_entry_form_uid_value').text(`(UID: ${entry.uid})`);
31452960
31462961 // Tri-state selector
31472962 handleEntryStateSelectorHelper({
31482963 entryStateSelector: templateheaderTemplate.find('select[name="entryStateSelector"]'),
31492964 entry, data, name, setWIOriginalDataValue, saveWorldInfo,
31502965 });
31512966
31522967 // Kill switch
31532968 handleEntryKillSwitchHelper({
31542969 entryKillSwitch: templateheaderTemplate.find('div[name="entryKillSwitch"]'),
31552970 entry, data, name, setWIOriginalDataValue, saveWorldInfo, template: headerTemplate,
3156- });
3157-
3158- // Exclude/prevent recursion
3159- handleMatchCheckboxHelper({ template, entry, fieldName: 'excludeRecursion', data, name });
3160- handleMatchCheckboxHelper({ template, entry, fieldName: 'preventRecursion', data, name });
3161-
3162- // Delay until recursion
3163- const delayUntilRecursionInput = template.find('input[name="delay_until_recursion"]');
3164- delayUntilRecursionInput.data('uid', entry.uid);
3165- const delayUntilRecursionLevelInput = template.find('input[name="delayUntilRecursionLevel"]');
3166- delayUntilRecursionLevelInput.data('uid', entry.uid);
3167- delayUntilRecursionInput.on('input', async function () {
3168- const uid = $(this).data('uid');
3169- const toggled = $(this).prop('checked');
3170- const value = toggled ? data.entries[uid].delayUntilRecursion || true : false;
3171- if (!toggled) delayUntilRecursionLevelInput.val('');
3172- data.entries[uid].delayUntilRecursion = value;
3173- setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
3174- await saveWorldInfo(name, data);
3175- });
3176- delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');
3177- delayUntilRecursionLevelInput.on('input', async function () {
3178- const uid = $(this).data('uid');
3179- const content = $(this).val();
3180- const value = content === '' ? (typeof data.entries[uid].delayUntilRecursion === 'boolean' ? data.entries[uid].delayUntilRecursion : true)
3181- : content === 1 ? true
3182- : !isNaN(Number(content)) ? Number(content)
3183- : false;
3184- data.entries[uid].delayUntilRecursion = value;
3185- setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
3186- await saveWorldInfo(name, data);
31872971 });
3188- delayUntilRecursionLevelInput.val(['number', 'string'].includes(typeof entry.delayUntilRecursion) ? entry.delayUntilRecursion : '').trigger('input');
31892972
31902973 // Duplicate/delete/move buttons
31912974 templateheaderTemplate.find('.duplicate_entry_button').data('uid', entry.uid).on('click', async function () {
31922975 const uid = $(this).data('uid');
31932976 const entryDup = duplicateWorldInfoEntry(data, uid);
31942977 if (entryDup) {
@@ -3196,7 +2979,7 @@ export async function getWorldEntry(name, data, entry) {
31962979 updateEditor(entryDup.uid);
31972980 }
31982981 });
31992982 templateheaderTemplate.find('.delete_entry_button').data('uid', entry.uid).on('click', async function (e) {
32002983 e.stopPropagation();
32012984 const uid = $(this).data('uid');
32022985 const deleted = await deleteWorldInfoEntry(data, uid);
@@ -3205,7 +2988,7 @@ export async function getWorldEntry(name, data, entry) {
32052988 await saveWorldInfo(name, data);
32062989 updateEditor(navigation_option.previous);
32072990 });
32082991 templateheaderTemplate.find('.move_entry_button').attr('data-uid', entry.uid).attr('data-current-world', name).on('click', async function (e) {
32092992 e.stopPropagation();
32102993 const sourceUid = $(this).attr('data-uid');
32112994 const sourceWorld = $(this).attr('data-current-world');
@@ -3257,8 +3040,167 @@ export async function getWorldEntry(name, data, entry) {
32573040 await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid);
32583041 });
32593042
3043+ let drawerInitialized = false;
3044+ let drawerDestroyTimeout = null;
3045+ headerTemplate.find('.inline-drawer-toggle').on('click', function () {
3046+ if (drawerDestroyTimeout) {
3047+ clearTimeout(drawerDestroyTimeout);
3048+ drawerDestroyTimeout = null;
3049+ }
3050+ if (drawerInitialized) {
3051+ drawerDestroyTimeout = setTimeout(() => {
3052+ // Drawer was reopened, so we don't destroy it
3053+ if (editOutlet.is(':visible')) {
3054+ return;
3055+ }
3056+ drawerInitialized = false;
3057+ clearEntryList(editOutlet);
3058+ }, debounce_timeout.relaxed);
3059+ } else {
3060+ drawerInitialized = true;
3061+ addEditorDrawerContent();
3062+ }
3063+ });
3064+
3065+ const editOutlet = headerTemplate.find('.inline-drawer-outlet');
3066+
3067+ function addEditorDrawerContent() {
3068+ const editTemplate = WI_ENTRY_EDIT_TEMPLATE.clone();
3069+
3070+ // UID display
3071+ editTemplate.find('.world_entry_form_uid_value').text(`(UID: ${entry.uid})`);
3072+
3073+ // Key inputs
3074+ const keyInput = enableKeysInputHelper({ template: editTemplate, entry, entryPropName: 'key', originalDataValueName: 'keys', name, data });
3075+ const keySecondaryInput = enableKeysInputHelper({ template: editTemplate, entry, entryPropName: 'keysecondary', originalDataValueName: 'secondary_keys', name, data });
3076+
3077+ // Key input switch
3078+ editTemplate.find('.switch_input_type_icon').on('click', function () {
3079+ power_user.wi_key_input_plaintext = !power_user.wi_key_input_plaintext;
3080+ saveSettingsDebounced();
3081+ const uid = ($(this).parents('.world_entry')).data('uid');
3082+ updateEditor(uid, false);
3083+ $(`.world_entry[uid="${uid}"] .inline-drawer-icon`).trigger('click');
3084+ }).each((_, icon) => {
3085+ $(icon).attr('title', $(icon).data(power_user.wi_key_input_plaintext ? 'tooltip-on' : 'tooltip-off'));
3086+ $(icon).text($(icon).data(power_user.wi_key_input_plaintext ? 'icon-on' : 'icon-off'));
3087+ });
3088+
3089+ // Probability toggle
3090+ handleProbabilityToggleHelper({
3091+ probabilityToggle: editTemplate.find('input[name="useProbability"]'),
3092+ data, entry, name,
3093+ probabilityInput: headerTemplate.find('input[name="probability"]'),
3094+ setWIOriginalDataValue, saveWorldInfo,
3095+ });
3096+
3097+ // Comment toggle
3098+ const commentToggle = editTemplate.find('input[name="addMemo"]');
3099+ commentToggle.data('uid', entry.uid);
3100+ commentToggle.on('input', async function () {
3101+ const uid = $(this).data('uid');
3102+ const value = $(this).prop('checked');
3103+ const commentContainer = $(this).closest('.world_entry').find('.commentContainer');
3104+ data.entries[uid].addMemo = value;
3105+ await saveWorldInfo(name, data);
3106+ value ? commentContainer.show() : commentContainer.hide();
3107+ });
3108+ commentToggle.prop('checked', true).trigger('input');
3109+ commentToggle.parent().hide();
3110+
3111+ // Logic AND/NOT
3112+ const selectiveLogicDropdown = editTemplate.find('select[name="entryLogicType"]');
3113+ selectiveLogicDropdown.data('uid', entry.uid);
3114+ selectiveLogicDropdown.on('click', e => e.stopPropagation());
3115+ selectiveLogicDropdown.on('input', async function () {
3116+ const uid = $(this).data('uid');
3117+ const value = Number($(this).val());
3118+ data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY;
3119+ setWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic);
3120+ await saveWorldInfo(name, data);
3121+ });
3122+ editTemplate.find(`select[name="entryLogicType"] option[value=${entry.selectiveLogic}]`).prop('selected', true).trigger('input');
3123+
3124+ // Selective
3125+ const selectiveInput = editTemplate.find('input[name="selective"]');
3126+ selectiveInput.data('uid', entry.uid);
3127+ selectiveInput.on('input', async function () {
3128+ const uid = $(this).data('uid');
3129+ const value = $(this).prop('checked');
3130+ data.entries[uid].selective = value;
3131+ setWIOriginalDataValue(data, uid, 'selective', data.entries[uid].selective);
3132+ await saveWorldInfo(name, data);
3133+ const keysecondary = $(this).closest('.world_entry').find('.keysecondary');
3134+ const keysecondarytextpole = $(this).closest('.world_entry').find('.keysecondarytextpole');
3135+ const keyprimaryselect = $(this).closest('.world_entry').find('.keyprimaryselect');
3136+ const keyprimaryHeight = keyprimaryselect.outerHeight();
3137+ keysecondarytextpole.css('height', keyprimaryHeight + 'px');
3138+ value ? keysecondary.show() : keysecondary.hide();
3139+ });
3140+ selectiveInput.prop('checked', true).trigger('input');
3141+ selectiveInput.parent().hide();
3142+
3143+ // Character filter
3144+ const characterFilterLabel = editTemplate.find('label[for="characterFilter"] > small');
3145+ characterFilterLabel.text(entry.characterFilter?.isExclude ? 'Exclude Character(s)' : 'Filter to Character(s)');
3146+ const characterExclusionInput = editTemplate.find('input[name="character_exclusion"]');
3147+ characterExclusionInput.data('uid', entry.uid);
3148+ characterExclusionInput.on('input', async function () {
3149+ const uid = $(this).data('uid');
3150+ const value = $(this).prop('checked');
3151+ characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)');
3152+ if (data.entries[uid].characterFilter) {
3153+ if (!value && data.entries[uid].characterFilter.names.length === 0 && data.entries[uid].characterFilter.tags.length === 0) {
3154+ delete data.entries[uid].characterFilter;
3155+ } else {
3156+ data.entries[uid].characterFilter.isExclude = value;
3157+ }
3158+ } else if (value) {
3159+ Object.assign(data.entries[uid], { characterFilter: { isExclude: true, names: [], tags: [] } });
3160+ }
3161+ if (data.entries[uid]?.characterFilter?.names?.length > 0) {
3162+ for (const name of [...data.entries[uid].characterFilter.names]) {
3163+ if (!getContext().characters.find(x => x.avatar.replace(/\.[^/.]+$/, '') === name)) {
3164+ data.entries[uid].characterFilter.names = data.entries[uid].characterFilter.names.filter(x => x !== name);
3165+ }
3166+ }
3167+ }
3168+ setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);
3169+ await saveWorldInfo(name, data);
3170+ });
3171+ characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input');
3172+
3173+ const characterFilter = editTemplate.find('select[name="characterFilter"]');
3174+ characterFilter.data('uid', entry.uid);
3175+ initCharacterFilterSelect2Helper(characterFilter, t);
3176+ fillCharacterAndTagOptionsHelper({ characterFilter, entry, getContext });
3177+ handleCharacterFilterChangeHelper({
3178+ characterFilter, data, entry, name, world_names, getContext, setWIOriginalDataValue, saveWorldInfo, t,
3179+ });
3180+
3181+ // Content
3182+ const counter = editTemplate.find('.world_entry_form_token_counter');
3183+ const countTokensDebounced = debounce(async function (counter, value) {
3184+ const numberOfTokens = await getTokenCountAsync(value);
3185+ $(counter).text(numberOfTokens);
3186+ }, debounce_timeout.relaxed);
3187+ const contentInputId = `world_entry_content_${entry.uid}`;
3188+ const contentInput = editTemplate.find('textarea[name="content"]');
3189+ contentInput.data('uid', entry.uid);
3190+ contentInput.attr('id', contentInputId);
3191+ contentInput.on('input', async function (_, { skipCount } = {}) {
3192+ const uid = $(this).data('uid');
3193+ const value = $(this).val();
3194+ data.entries[uid].content = value;
3195+ setWIOriginalDataValue(data, uid, 'content', data.entries[uid].content);
3196+ await saveWorldInfo(name, data);
3197+ if (!skipCount) countTokensDebounced(counter, value);
3198+ });
3199+ contentInput.val(entry.content).trigger('input', { skipCount: true });
3200+ editTemplate.find('.editor_maximize').attr('data-for', contentInputId);
3201+
32603202 // Scan depth
32613203 const scanDepthInput = templateeditTemplate.find('input[name="scanDepth"]');
32623204 scanDepthInput.data('uid', entry.uid);
32633205 scanDepthInput.on('input', async function () {
32643206 const uid = $(this).data('uid');
@@ -3280,30 +3222,111 @@ export async function getWorldEntry(name, data, entry) {
32803222 });
32813223 scanDepthInput.val(entry.scanDepth ?? null).trigger('input');
32823224
3225+ // Group
3226+ const groupInput = editTemplate.find('input[name="group"]');
3227+ groupInput.data('uid', entry.uid);
3228+ groupInput.on('input', async function () {
3229+ const uid = $(this).data('uid');
3230+ const value = String($(this).val()).trim();
3231+ data.entries[uid].group = value;
3232+ setWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group);
3233+ await saveWorldInfo(name, data);
3234+ });
3235+ groupInput.val(entry.group ?? '').trigger('input');
3236+ setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1);
3237+
3238+ // Inclusion priority
3239+ const groupOverrideInput = editTemplate.find('input[name="groupOverride"]');
3240+ groupOverrideInput.data('uid', entry.uid);
3241+ groupOverrideInput.on('input', async function () {
3242+ const uid = $(this).data('uid');
3243+ const value = $(this).prop('checked');
3244+ data.entries[uid].groupOverride = value;
3245+ setWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride);
3246+ await saveWorldInfo(name, data);
3247+ });
3248+ groupOverrideInput.prop('checked', entry.groupOverride).trigger('input');
3249+
3250+ // Group weight
3251+ handleNumberInputHelper({
3252+ inputElem: editTemplate.find('input[name="groupWeight"]'),
3253+ entry, entryKey: 'groupWeight', data, name, setWIOriginalDataValue, saveWorldInfo,
3254+ min: 1, max: 10000, clamp: true,
3255+ });
3256+
3257+ // Sticky, cooldown, delay
3258+ handleNumberInputHelper({
3259+ inputElem: editTemplate.find('input[name="sticky"]'),
3260+ entry, entryKey: 'sticky', data, name, setWIOriginalDataValue, saveWorldInfo,
3261+ min: 1, max: 10000, clamp: false,
3262+ });
3263+ handleNumberInputHelper({
3264+ inputElem: editTemplate.find('input[name="cooldown"]'),
3265+ entry, entryKey: 'cooldown', data, name, setWIOriginalDataValue, saveWorldInfo,
3266+ min: 1, max: 10000, clamp: false,
3267+ });
3268+ handleNumberInputHelper({
3269+ inputElem: editTemplate.find('input[name="delay"]'),
3270+ entry, entryKey: 'delay', data, name, setWIOriginalDataValue, saveWorldInfo,
3271+ min: 1, max: 10000, clamp: false,
3272+ });
3273+
3274+ // Exclude/prevent recursion
3275+ handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'excludeRecursion', data, name });
3276+ handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'preventRecursion', data, name });
3277+
3278+ // Delay until recursion
3279+ const delayUntilRecursionInput = editTemplate.find('input[name="delay_until_recursion"]');
3280+ delayUntilRecursionInput.data('uid', entry.uid);
3281+ const delayUntilRecursionLevelInput = editTemplate.find('input[name="delayUntilRecursionLevel"]');
3282+ delayUntilRecursionLevelInput.data('uid', entry.uid);
3283+ delayUntilRecursionInput.on('input', async function () {
3284+ const uid = $(this).data('uid');
3285+ const toggled = $(this).prop('checked');
3286+ const value = toggled ? data.entries[uid].delayUntilRecursion || true : false;
3287+ if (!toggled) delayUntilRecursionLevelInput.val('');
3288+ data.entries[uid].delayUntilRecursion = value;
3289+ setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
3290+ await saveWorldInfo(name, data);
3291+ });
3292+ delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');
3293+ delayUntilRecursionLevelInput.on('input', async function () {
3294+ const uid = $(this).data('uid');
3295+ const content = $(this).val();
3296+ const value = content === '' ? (typeof data.entries[uid].delayUntilRecursion === 'boolean' ? data.entries[uid].delayUntilRecursion : true)
3297+ : content === 1 ? true
3298+ : !isNaN(Number(content)) ? Number(content)
3299+ : false;
3300+ data.entries[uid].delayUntilRecursion = value;
3301+ setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
3302+ await saveWorldInfo(name, data);
3303+ });
3304+ delayUntilRecursionLevelInput.val(['number', 'string'].includes(typeof entry.delayUntilRecursion) ? entry.delayUntilRecursion : '').trigger('input');
3305+
32833306 // Boolean selects
32843307 handleBooleanSelectHelper({
32853308 selectElem: templateeditTemplate.find('select[name="caseSensitive"]'),
32863309 entry, entryKey: 'caseSensitive', data, name, setWIOriginalDataValue, saveWorldInfo,
32873310 });
32883311 handleBooleanSelectHelper({
32893312 selectElem: templateeditTemplate.find('select[name="matchWholeWords"]'),
32903313 entry, entryKey: 'matchWholeWords', data, name, setWIOriginalDataValue, saveWorldInfo,
32913314 });
32923315 handleBooleanSelectHelper({
32933316 selectElem: templateeditTemplate.find('select[name="useGroupScoring"]'),
32943317 entry, entryKey: 'useGroupScoring', data, name, setWIOriginalDataValue, saveWorldInfo,
32953318 });
32963319
32973320 // Match checkboxes
32983321 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchPersonaDescription', data, name });
32993322 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterDescription', data, name });
33003323 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterPersonality', data, name });
33013324 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterDepthPrompt', data, name });
33023325 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchScenario', data, name });
33033326 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCreatorNotes', data, name });
33043327
33053328 // Automation ID
33063329 const automationIdInput = templateeditTemplate.find('input[name="automationId"]');
33073330 automationIdInput.data('uid', entry.uid);
33083331 automationIdInput.on('input', async function () {
33093332 const uid = $(this).data('uid');
@@ -3315,9 +3338,17 @@ export async function getWorldEntry(name, data, entry) {
33153338 automationIdInput.val(entry.automationId ?? '').trigger('input');
33163339 setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1);
33173340
3318- template.find('.inline-drawer-content').css('display', 'none');
3341+ countTokensDebounced(counter, contentInput.val());
3342+ if (!keyInput.isFancy) initScrollHeight(keyInput.control);
3343+ if (!keySecondaryInput.isFancy) initScrollHeight(keySecondaryInput.control);
3344+
3345+ editTemplate.find('.inline-drawer-content').css('display', 'none');
3346+ editOutlet.append(editTemplate);
3347+ }
3348+
3349+ headerTemplate.find('.inline-drawer-content').css('display', 'none');
33193350
33203351 return templateheaderTemplate;
33213352}
33223353
33233354/**