From fda0e886e420c2eb07711ec01823a2eadb14de47 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 7 May 2024 05:44:18 +0200 Subject: [PATCH] WI custom styling for regex keys - WI custom style for regex keys - moved select2 styling to its own file --- public/css/select2-overrides.css | 70 ++++++++++++++++++++++++++++++++ public/css/world-info.css | 32 +++++++-------- public/index.html | 2 +- public/scripts/utils.js | 14 ++++++- public/scripts/world-info.js | 36 ++++++++++++---- public/style.css | 38 ----------------- 6 files changed, 127 insertions(+), 65 deletions(-) diff --git a/public/css/select2-overrides.css b/public/css/select2-overrides.css index cdab229d8..4f87450ef 100644 --- a/public/css/select2-overrides.css +++ b/public/css/select2-overrides.css @@ -171,3 +171,73 @@ .select2-results__option.select2-results__message::before { 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; +} \ No newline at end of file diff --git a/public/css/world-info.css b/public/css/world-info.css index ef814a104..3e63bae0f 100644 --- a/public/css/world-info.css +++ b/public/css/world-info.css @@ -197,20 +197,20 @@ display: none; } -#world_info+span.select2-container .select2-selection__choice__remove, -#world_info+span.select2-container .select2-selection__choice__display { - cursor: pointer; - transition: background-color 0.3s; +span.select2-container .select2-selection__choice__display:has(> .regex_item), +span.select2-container .select2-results__option:has(> .regex_item) { + background-color: #D27D2D30; +} + +.regex_item .regex_icon { + background-color: var(--black30a); color: var(--SmartThemeBodyColor); - background-color: var(--black50a); -} - -#world_info+span.select2-container .select2-selection__choice__display { - /* Fix weird alignment on the left side */ - margin-left: 1px; -} - -#world_info+span.select2-container .select2-selection__choice__remove:hover, -#world_info+span.select2-container .select2-selection__choice__display:hover { - background-color: var(--white30a); -} + border: 1px solid var(--SmartThemeBorderColor); + border-radius: 7px; + font-weight: bold; + font-size: calc(var(--mainFontSize) * 0.75); + padding: 0px 3px; + position: relative; + top: -1px; + margin-right: 3px; +} \ No newline at end of file diff --git a/public/index.html b/public/index.html index f6e9dd8c7..2ed236ed4 100644 --- a/public/index.html +++ b/public/index.html @@ -3310,7 +3310,7 @@ Active World(s) for all chats
-
diff --git a/public/scripts/utils.js b/public/scripts/utils.js index b8f2d5b6e..ca5350db2 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -1450,6 +1450,18 @@ export function includesIgnoreCaseAndAccents(text, searchTerm) { 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 * @@ -1462,7 +1474,7 @@ export function includesIgnoreCaseAndAccents(text, searchTerm) { export function select2ModifyOptions(element, items, { select = false, changeEventArgs = null } = {}) { if (!items.length) return; /** @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 => { // Set the value, creating a new option if necessary diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index ce46a40e2..76f355693 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -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 } 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 { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.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. * @@ -1162,12 +1168,12 @@ function splitKeywordsAndRegexes(input) { // 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. // No need for validation here - const addFindCallback = (/** @type {{id: string, text: string}} */ item) => { - keywordsAndRegexes.push(item.id); + const addFindCallback = (/** @type {Select2Option} */ item) => { + keywordsAndRegexes.push(item.text); } 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; } @@ -1177,7 +1183,7 @@ function splitKeywordsAndRegexes(input) { * * @param {{_type: string, term: string}} input - The typed input * @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 */ function customTokenizer(input, _selection, callback) { @@ -1205,12 +1211,14 @@ function customTokenizer(input, _selection, callback) { // So now the comma really means the token is done. // We take the token up till now, and insert it. Empty will be skipped. 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. - if (token.startsWith('/') && !isValidRegex(token)) { + if (token.startsWith('/') && !isRegex) { const tokens = token.split(',').map(x => x.trim()); tokens.forEach(x => callback({ id: x, text: x })); } else { - callback({ id: token, text: token }); + callback({ id: getSelect2OptionId(token), text: token }); } } @@ -1289,17 +1297,26 @@ function getWorldEntry(name, data, entry) { event.stopPropagation(); }); + function templateStyling(/** @type {Select2Option} */ item) { + const isRegex = isValidRegex(item.text); + if (!isRegex) return item.text; + return $('').addClass('regex_item').text(item.text) + .prepend($('').addClass('regex_icon').text("•*").attr('title', 'Regex')); + } + if (!isMobile()) { input.select2({ tags: true, tokenSeparators: [','], tokenizer: customTokenizer, placeholder: input.attr('placeholder'), + templateResult: templateStyling, + templateSelection: templateStyling, }); input.on('change', function (_, { skipReset, noSave } = {}) { const uid = $(this).data('uid'); /** @type {string[]} */ - const keys = ($(this).select2('data')).map(x => x.id); + const keys = ($(this).select2('data')).map(x => x.text); !skipReset && resetScrollHeight(this); if (!noSave) { @@ -3421,8 +3438,9 @@ jQuery(() => { if ($(event.target).hasClass('select2-selection__choice__display')) { 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'); + setTimeout(() => $('#world_info + span.select2-container textarea').trigger('blur'), debounce_timeout.quick); const name = $(event.target).text(); const selectedIndex = world_names.indexOf(name); diff --git a/public/style.css b/public/style.css index 52cddb605..3a32569c8 100644 --- a/public/style.css +++ b/public/style.css @@ -4035,42 +4035,4 @@ body:not(.movingUI) .drawer-content.maximized { height: 100vh; 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; } \ No newline at end of file