mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
WI custom styling for regex keys
- WI custom style for regex keys - moved select2 styling to its own file
This commit is contained in:
@ -171,3 +171,73 @@
|
|||||||
.select2-results__option.select2-results__message::before {
|
.select2-results__option.select2-results__message::before {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select2-selection__choice__display {
|
||||||
|
/* Fix weird alignment on the left side */
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling for choice remove icon */
|
||||||
|
span.select2.select2-container .select2-selection__choice__remove {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
color: var(--SmartThemeBodyColor);
|
||||||
|
background-color: var(--black50a);
|
||||||
|
}
|
||||||
|
|
||||||
|
span.select2.select2-container .select2-selection__choice__remove:hover {
|
||||||
|
color: var(--SmartThemeBodyColor);
|
||||||
|
background-color: var(--white30a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom class to support styling to show clickable choice options */
|
||||||
|
.select2_choice_clickable+span.select2-container .select2-selection__choice__display {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
color: var(--SmartThemeBodyColor);
|
||||||
|
background-color: var(--black50a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2_choice_clickable+span.select2-container .select2-selection__choice__display:hover {
|
||||||
|
background-color: var(--white30a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom class to support same line multi inputs of select2 controls */
|
||||||
|
.select2_multi_sameline+span.select2-container .select2-selection--multiple {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
|
||||||
|
/* Allow search placeholder to take up all space if needed */
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
/* Fix weird styling choice or huge margin around selected options */
|
||||||
|
margin-block-start: 2px;
|
||||||
|
margin-block-end: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search__field {
|
||||||
|
/* Min height to reserve spacing */
|
||||||
|
min-height: calc(var(--mainFontSize) + 13px);
|
||||||
|
/* Min width to be clickable */
|
||||||
|
min-width: 4em;
|
||||||
|
align-content: center;
|
||||||
|
/* Fix search textarea alignment issue with UL elements */
|
||||||
|
margin-top: 0px;
|
||||||
|
height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
/* Min height to reserve spacing */
|
||||||
|
min-height: calc(var(--mainFontSize) + 13px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make search bar invisible unless the select2 is active, to save space */
|
||||||
|
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2_multi_sameline+span.select2-container.select2-container--focus .select2-selection--multiple .select2-search--inline {
|
||||||
|
height: unset;
|
||||||
|
}
|
@ -197,20 +197,20 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#world_info+span.select2-container .select2-selection__choice__remove,
|
span.select2-container .select2-selection__choice__display:has(> .regex_item),
|
||||||
#world_info+span.select2-container .select2-selection__choice__display {
|
span.select2-container .select2-results__option:has(> .regex_item) {
|
||||||
cursor: pointer;
|
background-color: #D27D2D30;
|
||||||
transition: background-color 0.3s;
|
}
|
||||||
|
|
||||||
|
.regex_item .regex_icon {
|
||||||
|
background-color: var(--black30a);
|
||||||
color: var(--SmartThemeBodyColor);
|
color: var(--SmartThemeBodyColor);
|
||||||
background-color: var(--black50a);
|
border: 1px solid var(--SmartThemeBorderColor);
|
||||||
}
|
border-radius: 7px;
|
||||||
|
font-weight: bold;
|
||||||
#world_info+span.select2-container .select2-selection__choice__display {
|
font-size: calc(var(--mainFontSize) * 0.75);
|
||||||
/* Fix weird alignment on the left side */
|
padding: 0px 3px;
|
||||||
margin-left: 1px;
|
position: relative;
|
||||||
}
|
top: -1px;
|
||||||
|
margin-right: 3px;
|
||||||
#world_info+span.select2-container .select2-selection__choice__remove:hover,
|
}
|
||||||
#world_info+span.select2-container .select2-selection__choice__display:hover {
|
|
||||||
background-color: var(--white30a);
|
|
||||||
}
|
|
@ -3310,7 +3310,7 @@
|
|||||||
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
|
<span data-i18n="Active World(s) for all chats"><small>Active World(s) for all chats</small></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="range-block-range">
|
<div class="range-block-range">
|
||||||
<select id="world_info" class="select2_multi_sameline" multiple>
|
<select id="world_info" class="select2_multi_sameline select2_choice_clickable" multiple>
|
||||||
<option value="" data-i18n="-- World Info not found --">-- World Info not found -- </option>
|
<option value="" data-i18n="-- World Info not found --">-- World Info not found -- </option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1450,6 +1450,18 @@ export function includesIgnoreCaseAndAccents(text, searchTerm) {
|
|||||||
return normalizedText.includes(normalizedSearchTerm);
|
return normalizedText.includes(normalizedSearchTerm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique hash as ID for a select2 option text
|
||||||
|
*
|
||||||
|
* @param {string} option - The option
|
||||||
|
* @returns {string} A hashed version of that option
|
||||||
|
*/
|
||||||
|
export function getSelect2OptionId(option) {
|
||||||
|
return String(getStringHash(option));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the select2 options by adding not existing one and optionally selecting them
|
* Modifies the select2 options by adding not existing one and optionally selecting them
|
||||||
*
|
*
|
||||||
@ -1462,7 +1474,7 @@ export function includesIgnoreCaseAndAccents(text, searchTerm) {
|
|||||||
export function select2ModifyOptions(element, items, { select = false, changeEventArgs = null } = {}) {
|
export function select2ModifyOptions(element, items, { select = false, changeEventArgs = null } = {}) {
|
||||||
if (!items.length) return;
|
if (!items.length) return;
|
||||||
/** @type {{id: string, text: string}[]} */
|
/** @type {{id: string, text: string}[]} */
|
||||||
const dataItems = items.map(x => typeof x === 'string' ? { id: x, text: x } : x);
|
const dataItems = items.map(x => typeof x === 'string' ? { id: getSelect2OptionId(x), text: x } : x);
|
||||||
|
|
||||||
dataItems.forEach(item => {
|
dataItems.forEach(item => {
|
||||||
// Set the value, creating a new option if necessary
|
// Set the value, creating a new option if necessary
|
||||||
|
@ -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 { 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 } from './utils.js';
|
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getStringHash, getSelect2OptionId } from './utils.js';
|
||||||
import { extension_settings, getContext } from './extensions.js';
|
import { extension_settings, getContext } from './extensions.js';
|
||||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js';
|
||||||
import { registerSlashCommand } from './slash-commands.js';
|
import { registerSlashCommand } from './slash-commands.js';
|
||||||
@ -1146,6 +1146,12 @@ function deleteOriginalDataValue(data, uid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} Select2Option The option object for select2 controls
|
||||||
|
* @property {string} id - The unique ID inside this select
|
||||||
|
* @property {string} text - The text for this option
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits a given input string that contains one or more keywords or regexes, separated by commas.
|
* Splits a given input string that contains one or more keywords or regexes, separated by commas.
|
||||||
*
|
*
|
||||||
@ -1162,12 +1168,12 @@ function splitKeywordsAndRegexes(input) {
|
|||||||
// We can make this easy. Instead of writing another function to find and parse regexes,
|
// We can make this easy. Instead of writing another function to find and parse regexes,
|
||||||
// we gonna utilize the custom tokenizer that also handles the input.
|
// we gonna utilize the custom tokenizer that also handles the input.
|
||||||
// No need for validation here
|
// No need for validation here
|
||||||
const addFindCallback = (/** @type {{id: string, text: string}} */ item) => {
|
const addFindCallback = (/** @type {Select2Option} */ item) => {
|
||||||
keywordsAndRegexes.push(item.id);
|
keywordsAndRegexes.push(item.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { term } = customTokenizer({ _type: 'custom_call', term: input }, undefined, addFindCallback);
|
const { term } = customTokenizer({ _type: 'custom_call', term: input }, undefined, addFindCallback);
|
||||||
addFindCallback({ id: term.trim(), text: term.trim() });
|
addFindCallback({ id: getSelect2OptionId(term.trim()), text: term.trim() });
|
||||||
|
|
||||||
return keywordsAndRegexes;
|
return keywordsAndRegexes;
|
||||||
}
|
}
|
||||||
@ -1177,7 +1183,7 @@ function splitKeywordsAndRegexes(input) {
|
|||||||
*
|
*
|
||||||
* @param {{_type: string, term: string}} input - The typed input
|
* @param {{_type: string, term: string}} input - The typed input
|
||||||
* @param {{options: object}} _selection - The selection even object (?)
|
* @param {{options: object}} _selection - The selection even object (?)
|
||||||
* @param {function} callback - The original callback function to call if an item should be inserted
|
* @param {function(Select2Option):void} callback - The original callback function to call if an item should be inserted
|
||||||
* @returns {{term: string}} - The remaining part that is untokenized in the textbox
|
* @returns {{term: string}} - The remaining part that is untokenized in the textbox
|
||||||
*/
|
*/
|
||||||
function customTokenizer(input, _selection, callback) {
|
function customTokenizer(input, _selection, callback) {
|
||||||
@ -1205,12 +1211,14 @@ function customTokenizer(input, _selection, callback) {
|
|||||||
// So now the comma really means the token is done.
|
// So now the comma really means the token is done.
|
||||||
// We take the token up till now, and insert it. Empty will be skipped.
|
// We take the token up till now, and insert it. Empty will be skipped.
|
||||||
if (token) {
|
if (token) {
|
||||||
|
const isRegex = isValidRegex(token);
|
||||||
|
|
||||||
// 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.
|
// 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('/') && !isValidRegex(token)) {
|
if (token.startsWith('/') && !isRegex) {
|
||||||
const tokens = token.split(',').map(x => x.trim());
|
const tokens = token.split(',').map(x => x.trim());
|
||||||
tokens.forEach(x => callback({ id: x, text: x }));
|
tokens.forEach(x => callback({ id: x, text: x }));
|
||||||
} else {
|
} else {
|
||||||
callback({ id: token, text: token });
|
callback({ id: getSelect2OptionId(token), text: token });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1289,17 +1297,26 @@ function getWorldEntry(name, data, entry) {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function templateStyling(/** @type {Select2Option} */ item) {
|
||||||
|
const isRegex = isValidRegex(item.text);
|
||||||
|
if (!isRegex) return item.text;
|
||||||
|
return $('<span>').addClass('regex_item').text(item.text)
|
||||||
|
.prepend($('<span>').addClass('regex_icon').text("•*").attr('title', 'Regex'));
|
||||||
|
}
|
||||||
|
|
||||||
if (!isMobile()) {
|
if (!isMobile()) {
|
||||||
input.select2({
|
input.select2({
|
||||||
tags: true,
|
tags: true,
|
||||||
tokenSeparators: [','],
|
tokenSeparators: [','],
|
||||||
tokenizer: customTokenizer,
|
tokenizer: customTokenizer,
|
||||||
placeholder: input.attr('placeholder'),
|
placeholder: input.attr('placeholder'),
|
||||||
|
templateResult: templateStyling,
|
||||||
|
templateSelection: templateStyling,
|
||||||
});
|
});
|
||||||
input.on('change', function (_, { skipReset, noSave } = {}) {
|
input.on('change', function (_, { skipReset, noSave } = {}) {
|
||||||
const uid = $(this).data('uid');
|
const uid = $(this).data('uid');
|
||||||
/** @type {string[]} */
|
/** @type {string[]} */
|
||||||
const keys = ($(this).select2('data')).map(x => x.id);
|
const keys = ($(this).select2('data')).map(x => x.text);
|
||||||
|
|
||||||
!skipReset && resetScrollHeight(this);
|
!skipReset && resetScrollHeight(this);
|
||||||
if (!noSave) {
|
if (!noSave) {
|
||||||
@ -3421,8 +3438,9 @@ jQuery(() => {
|
|||||||
if ($(event.target).hasClass('select2-selection__choice__display')) {
|
if ($(event.target).hasClass('select2-selection__choice__display')) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// select2 still bubbles the event to open the dropdown. So we close it here
|
// select2 still bubbles the event to open the dropdown. So we close it here and remove focus
|
||||||
$('#world_info').select2('close');
|
$('#world_info').select2('close');
|
||||||
|
setTimeout(() => $('#world_info + span.select2-container textarea').trigger('blur'), debounce_timeout.quick);
|
||||||
|
|
||||||
const name = $(event.target).text();
|
const name = $(event.target).text();
|
||||||
const selectedIndex = world_names.indexOf(name);
|
const selectedIndex = world_names.indexOf(name);
|
||||||
|
@ -4035,42 +4035,4 @@ body:not(.movingUI) .drawer-content.maximized {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Custom class to support same line multi inputs of select2 controls */
|
|
||||||
.select2_multi_sameline+span.select2-container .select2-selection--multiple {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
|
|
||||||
flex-grow: 1; /* Allow search placeholder to take up all space if needed */
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
|
|
||||||
/* Fix weird styling choice or huge margin around selected options */
|
|
||||||
margin-block-start: 2px;
|
|
||||||
margin-block-end: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search__field {
|
|
||||||
min-height: calc(var(--mainFontSize) + 13px);
|
|
||||||
min-width: 4em;
|
|
||||||
align-content: center;
|
|
||||||
/* Fix search textarea alignment issue with UL elements */
|
|
||||||
margin-top: 0px;
|
|
||||||
height: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-selection__rendered {
|
|
||||||
min-height: calc(var(--mainFontSize) + 13px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make search bar invisible unless the select2 is active, to save space */
|
|
||||||
.select2_multi_sameline+span.select2-container .select2-selection--multiple .select2-search--inline {
|
|
||||||
height: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2_multi_sameline+span.select2-container.select2-container--open .select2-selection--multiple .select2-search--inline {
|
|
||||||
height: unset;
|
|
||||||
}
|
}
|
Reference in New Issue
Block a user