mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-01-20 21:41:32 +01:00
WI key input mode switch fancy/plaintext
- Implemented switch between fancy and plaintext input controls - Fixed splitting keys into regexes index issue - Fixed focus falsely adding text as key
This commit is contained in:
parent
5426431adf
commit
00ce078630
@ -76,6 +76,7 @@
|
||||
.world_entry_form_control {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.world_entry_thin_controls {
|
||||
@ -222,4 +223,26 @@ span.select2-container .select2-results__option:has(> .result_block .regex_item)
|
||||
.select2-results__option .item_count {
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
select.keyselect+span.select2-container .select2-selection--multiple {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.switch_input_type_icon {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
height: calc(100% - var(--mainFontSize));
|
||||
width: 25px;
|
||||
margin-right: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 1px;
|
||||
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
font-size: 1em;
|
||||
|
||||
color: var(--SmartThemeBodyColor);
|
||||
}
|
||||
|
@ -5206,6 +5206,9 @@
|
||||
<small class="textAlignCenter" data-i18n="Primary Keywords">Primary Keywords</small>
|
||||
<select class="keyprimaryselect keyselect select2_multi_sameline" name="key" data-i18n="[placeholder]Keywords or Regexes" placeholder="Keywords or Regexes" multiple="multiple"></select>
|
||||
<textarea class="text_pole keyprimarytextpole mobile" name="key" rows="1" data-i18n="[placeholder]Comma separated list" placeholder="Comma separated list" maxlength="2000" style="display: none;"></textarea>
|
||||
<button type="button" class="switch_input_type_icon" tabindex="-1" title="Switch to plaintext mode" data-icon-on="✨" data-icon-off="⌨️" data-tooltip-on="Switch to fancy mode" data-tooltip-off="Switch to plaintext mode">
|
||||
⌨️
|
||||
</button>
|
||||
</div>
|
||||
<div class="world_entry_form_control">
|
||||
<small class="textAlignCenter" data-i18n="Logic">Logic</small>
|
||||
@ -5225,6 +5228,9 @@
|
||||
<small class="textAlignCenter" data-i18n="Optional Filter">Optional Filter</small>
|
||||
<select class="keysecondaryselect keyselect select2_multi_sameline" name="keysecondary" data-i18n="[placeholder]Keywords or Regexes (ignored if empty)" placeholder="Keywords or Regexes (ignored if empty)" multiple="multiple"></select>
|
||||
<textarea class="text_pole keysecondarytextpole mobile" name="keysecondary" rows="1" data-i18n="[placeholder]Comma separated list (ignored if empty)" placeholder="Comma separated list (ignored if empty)" maxlength="2000" style="display: none;"></textarea>
|
||||
<button type="button" class="switch_input_type_icon" tabindex="-1" title="Switch to plaintext mode" data-icon-on="✨" data-icon-off="⌨️" data-tooltip-on="Switch to fancy mode" data-tooltip-off="Switch to plaintext mode">
|
||||
⌨️
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div name="perEntryOverridesBlock" class="flex-container wide100p alignitemscenter">
|
||||
|
@ -1520,7 +1520,7 @@ export function select2ModifyOptions(element, items, { select = false, changeEve
|
||||
* Can be used on a single global array, querying data from the server or anything similar.
|
||||
*
|
||||
* @param {function():Select2Option[]} dataProvider - The provider/function to retrieve the data - can be as simple as "() => myData" for arrays
|
||||
* @return {{transport: function}} The ajax object with the transport function to use on the select2 ajax property
|
||||
* @return {{transport: (params, success, failure) => any}} The ajax object with the transport function to use on the select2 ajax property
|
||||
*/
|
||||
export function dynamicSelect2DataViaAjax(dataProvider) {
|
||||
function dynamicSelect2DataTransport(params, success, failure) {
|
||||
@ -1543,11 +1543,21 @@ export function dynamicSelect2DataViaAjax(dataProvider) {
|
||||
return ajax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given control is a select2 choice element - meaning one of the results being displayed in the select multi select box
|
||||
* @param {JQuery<HTMLElement>|HTMLElement} element - The element to check
|
||||
* @returns {boolean} Whether this is a choice element
|
||||
*/
|
||||
export function isSelect2ChoiceElement(element) {
|
||||
const $element = $(element);
|
||||
return ($element.hasClass('select2-selection__choice__display') || $element.parents('.select2-selection__choice__display').length > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes a 'click' event handler to the choice elements of a select2 multi-select control
|
||||
*
|
||||
* @param {JQuery<HTMLElement>} control The original control the select2 was applied to
|
||||
* @param {function(EventTarget):void} action - The action to execute when a choice element is clicked
|
||||
* @param {function(HTMLElement):void} action - The action to execute when a choice element is clicked
|
||||
* @param {object} options - Optional parameters
|
||||
* @param {boolean} [options.buttonStyle=false] - Whether the choices should be styles as a clickable button with color and hover transition, instead of just changed cursor
|
||||
* @param {boolean} [options.closeDrawer=false] - Whether the drawer should be closed and focus removed after the choice item was clicked
|
||||
@ -1561,8 +1571,8 @@ export function select2ChoiceClickSubscribe(control, action, { buttonStyle = fal
|
||||
// Get the real container below and create a click handler on that one
|
||||
const select2Container = control.next('span.select2-container');
|
||||
select2Container.on('click', function (event) {
|
||||
const $target = $(event.target);
|
||||
if ($target.hasClass('select2-selection__choice__display') || $target.parents('.select2-selection__choice__display')) {
|
||||
const isChoice = isSelect2ChoiceElement(event.target);
|
||||
if (isChoice) {
|
||||
event.preventDefault();
|
||||
|
||||
// select2 still bubbles the event to open the dropdown. So we close it here and remove focus if we want that
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js';
|
||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getStringHash, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe } from './utils.js';
|
||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe } from './utils.js';
|
||||
import { extension_settings, getContext } from './extensions.js';
|
||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
||||
import { isMobile } from './RossAscends-mods.js';
|
||||
@ -69,7 +69,7 @@ const saveSettingsDebounced = debounce(() => {
|
||||
saveSettings();
|
||||
}, debounce_timeout.relaxed);
|
||||
const sortFn = (a, b) => b.order - a.order;
|
||||
let updateEditor = (navigation) => { console.debug('Triggered WI navigation', navigation); };
|
||||
let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); };
|
||||
|
||||
// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.
|
||||
const worldInfoFilter = new FilterHelper(() => updateEditor());
|
||||
@ -1022,8 +1022,8 @@ function updateWorldEntryKeyOptionsCache(keyOptions, { remove = false, reset = f
|
||||
worldEntryKeyOptionsCache.sort((a, b) => b.count - a.count || a.text.localeCompare(b.text));
|
||||
}
|
||||
|
||||
function displayWorldEntries(name, data, navigation = navigation_option.none) {
|
||||
updateEditor = (navigation) => displayWorldEntries(name, data, navigation);
|
||||
function displayWorldEntries(name, data, navigation = navigation_option.none, flashOnNav = true) {
|
||||
updateEditor = (navigation, flashOnNav = true) => displayWorldEntries(name, data, navigation, flashOnNav);
|
||||
|
||||
const worldEntriesList = $('#world_popup_entries_list');
|
||||
|
||||
@ -1156,7 +1156,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none) {
|
||||
const parentOffset = element.parent().offset();
|
||||
const scrollOffset = elementOffset.top - parentOffset.top;
|
||||
$('#WorldInfo').scrollTop(scrollOffset);
|
||||
flashHighlight(element);
|
||||
if (flashOnNav) flashHighlight(element);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1364,7 +1364,10 @@ function splitKeywordsAndRegexes(input) {
|
||||
}
|
||||
|
||||
const { term } = customTokenizer({ _type: 'custom_call', term: input }, undefined, addFindCallback);
|
||||
addFindCallback({ id: getSelect2OptionId(term.trim()), text: term.trim() });
|
||||
const finalTerm = term.trim();
|
||||
if (finalTerm) {
|
||||
addFindCallback({ id: getSelect2OptionId(finalTerm), text: finalTerm });
|
||||
}
|
||||
|
||||
return keywordsAndRegexes;
|
||||
}
|
||||
@ -1407,7 +1410,7 @@ function customTokenizer(input, _selection, callback) {
|
||||
// Last chance to check for valid regex again. Because it might have been valid while typing, but now is not valid anymore and contains commas we need to split.
|
||||
if (token.startsWith('/') && !isRegex) {
|
||||
const tokens = token.split(',').map(x => x.trim());
|
||||
tokens.forEach(x => callback({ id: x, text: x }));
|
||||
tokens.forEach(x => callback({ id: getSelect2OptionId(x), text: x }));
|
||||
} else {
|
||||
callback({ id: getSelect2OptionId(token), text: token });
|
||||
}
|
||||
@ -1415,6 +1418,7 @@ function customTokenizer(input, _selection, callback) {
|
||||
|
||||
// Now remove the token from the current input, and the comma too
|
||||
current = current.slice(i + 1);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1481,7 +1485,8 @@ function getWorldEntry(name, data, entry) {
|
||||
|
||||
/** Function to build the keys input controls @param {string} entryPropName @param {string} originalDataValueName */
|
||||
function enableKeysInput(entryPropName, originalDataValueName) {
|
||||
const input = !isMobile() ? template.find(`select[name="${entryPropName}"]`) : template.find(`textarea[name="${entryPropName}"]`);
|
||||
const isFancyInput = !isMobile() && !power_user.wi_key_input_plaintext;
|
||||
const input = isFancyInput ? template.find(`select[name="${entryPropName}"]`) : template.find(`textarea[name="${entryPropName}"]`);
|
||||
input.data('uid', entry.uid);
|
||||
input.on('click', function (event) {
|
||||
// Prevent closing the drawer on clicking the input
|
||||
@ -1507,7 +1512,7 @@ function getWorldEntry(name, data, entry) {
|
||||
return content;
|
||||
}
|
||||
|
||||
if (!isMobile()) {
|
||||
if (isFancyInput) {
|
||||
input.select2({
|
||||
ajax: dynamicSelect2DataViaAjax(() => worldEntryKeyOptionsCache),
|
||||
tags: true,
|
||||
@ -1557,14 +1562,15 @@ function getWorldEntry(name, data, entry) {
|
||||
template.find(`select[name="${entryPropName}"]`).hide();
|
||||
input.show();
|
||||
|
||||
input.on('input', function (_, { skipReset } = {}) {
|
||||
input.on('input', function (_, { skipReset, noSave } = {}) {
|
||||
const uid = $(this).data('uid');
|
||||
const value = String($(this).val());
|
||||
!skipReset && resetScrollHeight(this);
|
||||
data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value);
|
||||
|
||||
setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
|
||||
saveWorldInfo(name, data);
|
||||
if (!noSave) {
|
||||
data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value);
|
||||
setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
|
||||
saveWorldInfo(name, data);
|
||||
}
|
||||
});
|
||||
input.val(entry[entryPropName].join(', ')).trigger('input', { skipReset: true });
|
||||
}
|
||||
@ -1576,6 +1582,23 @@ function getWorldEntry(name, data, entry) {
|
||||
// keysecondary
|
||||
enableKeysInput("keysecondary", "secondary_keys");
|
||||
|
||||
// draw key input switch button
|
||||
template.find('.switch_input_type_icon').on('click', function () {
|
||||
power_user.wi_key_input_plaintext = !power_user.wi_key_input_plaintext;
|
||||
saveSettingsDebounced();
|
||||
|
||||
// Just redraw the panel
|
||||
const uid = ($(this).parents('.world_entry')).data('uid');
|
||||
updateEditor(uid, false);
|
||||
|
||||
$(`.world_entry[uid="${uid}"] .inline-drawer-icon`).trigger('click');
|
||||
// setTimeout(() => {
|
||||
// }, debounce_timeout.standard);
|
||||
}).each((_, icon) => {
|
||||
$(icon).attr('title', $(icon).data(power_user.wi_key_input_plaintext ? 'tooltip-on' : 'tooltip-off'));
|
||||
$(icon).text($(icon).data(power_user.wi_key_input_plaintext ? 'icon-on' : 'icon-off'));
|
||||
});
|
||||
|
||||
// logic AND/NOT
|
||||
const selectiveLogicDropdown = template.find('select[name="entryLogicType"]');
|
||||
selectiveLogicDropdown.data('uid', entry.uid);
|
||||
|
Loading…
Reference in New Issue
Block a user