diff --git a/public/index.html b/public/index.html index e46d9bae9..c52a9c656 100644 --- a/public/index.html +++ b/public/index.html @@ -1367,6 +1367,7 @@
+

Extension settings

@@ -1439,13 +1438,14 @@
-
+
 

-
- -
+
+
Tags
+ +
@@ -1529,35 +1529,6 @@

Members

- -
-
-
- Avatar -
-
-
-
-
-
-
-
-
-
- -
-
-
- -
-
-
-
-
- - -
-
@@ -1815,7 +1789,14 @@ -
+
+ + + + +
+ +
@@ -1823,7 +1804,7 @@
-
+
@@ -1856,7 +1837,7 @@
-
+
img1
@@ -1877,8 +1858,37 @@
+
+
+
+ Avatar +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+ + +
+
+ -
+
CHAR is typing
diff --git a/public/script.js b/public/script.js index 8421d264c..8ecfdbc17 100644 --- a/public/script.js +++ b/public/script.js @@ -93,6 +93,7 @@ import { import { debounce, delay } from "./scripts/utils.js"; import { extension_settings, loadExtensionSettings } from "./scripts/extensions.js"; import { executeSlashCommands, getSlashCommandsHelp } from "./scripts/slash-commands.js"; +import { tag_map, tags, loadTagsSettings, printTags, isElementTagged } from "./scripts/tags.js"; //exporting functions and vars for mods export { @@ -277,6 +278,8 @@ const system_messages = { '', 'Type /? in any chat to get help on message formatting commands.', '

Still have questions or suggestions left?

', + 'SillyTavern Community Discord', + '
', 'Post a GitHub issue.', '
', 'Contact the developers.' @@ -667,9 +670,8 @@ function printCharacters() { this_avatar = getThumbnailUrl('avatar', item.avatar); } //RossAscends: changed 'prepend' to 'append' to make alphabetical sorting display correctly. $("#rm_print_characters_block").append( - `
-
+
${item.name} ${item.fav == "true" ? '' : ''}
` @@ -677,6 +679,7 @@ function printCharacters() { //console.log('printcharacters() -- printing -- ChID '+i+' ('+item.name+')'); }); $("#rm_print_characters_block").prepend(`
`); + printTags(); printGroups(); sortCharactersList(); } @@ -2825,6 +2828,8 @@ async function getSettings(type) { // Load power user settings loadPowerUserSettings(settings, data); + // Load- character tags + loadTagsSettings(settings); //Enable GUI deference settings if GUI is selected for Kobold if (main_api === "kobold") { @@ -2918,10 +2923,12 @@ async function saveSettings(type) { power_user: power_user, poe_settings: poe_settings, extension_settings: extension_settings, + tags: tags, + tag_map: tag_map, ...nai_settings, ...kai_settings, ...oai_settings, - }), + }, null, 4), beforeSend: function () { //console.log('saveSettings() -- active_character -- '+active_character); if (type == "change_name") { @@ -3811,16 +3818,38 @@ $(document).ready(function () { $("#character_search_bar").on("input", function () { const selector = ['#rm_print_characters_block .character_select', '#rm_print_characters_block .group_select'].join(','); const searchValue = $(this).val().trim().toLowerCase(); + const selectedTagId = $('#rm_tag_filter .tag.selected').attr('id'); if (!searchValue) { - $(selector).show(); + $(selector).each(function () { + if (selectedTagId && !isElementTagged(this, selectedTagId)) { + $(this).hide(); + } + else { + $(this).show(); + } + }) } else { $(selector).each(function () { - $(this).children(".ch_name").text().toLowerCase().includes(searchValue) - ? $(this).show() - : $(this).hide(); + const isValidSearch = $(this).children(".ch_name").text().toLowerCase().includes(searchValue); + + if (isValidSearch) { + if (selectedTagId && !isElementTagged(this, selectedTagId)) { + $(this).hide(); + } + else { + $(this).show(); + } + } + else { + $(this).hide(); + } }); } + + if (selectedTagId) { + applyFilterToList(selectedTagId) + } }); $("#filter_by_fav").click(function () { diff --git a/public/scripts/tags.js b/public/scripts/tags.js index dcdfe031e..74812441f 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -1,49 +1,253 @@ +import { characters, saveSettingsDebounced, this_chid } from "../script.js"; +import { selected_group } from "./group-chats.js"; + +export { + tags, + tag_map, + loadTagsSettings, + printTags, + isElementTagged, +}; + const TAG_COLORS = [ - '#FFB6C1', // Light Pink - '#FFECB3', // Light Yellow - '#FFE4B5', // Cream - '#B2EBF2', // Powder Blue - '#E0FFFF', // Light Sky Blue - '#FBB4B9', // Lavender - '#FFF9C4', // Floral White - '#DDA0DD', // Plum - '#DA70D6', // Orchid - '#D2B48C', // Tan - '#FAEBD7', // Antique White - '#FFEFD5', // Papaya Whip - '#CD853F', // Peru - '#B2DFEE', // Sky Blue - '#FFFFC2', // Parchment - '#FDF5E6', // Old Lace + "#dd0a36", // Red + "#ff6633", // Orange + "#5f9ea0", // Teal Green + "#1e90ff", // Light Blue + "#990066", // Plum + "#8c00ff", // Fuchsia + "#00ffff", // Aqua + "#0f4ecc", // Teal + "#2f4b1c", // Green + "#3366e5", // Dodger Blue + "#36c3a1", // Mint Green + "#995511", // Terracotta + "#ab47bc", // Plum RGBA + "#805451", // Mulberry + "#ff8c69", // Salmon + "#ba55d3", // Magenta + "#b3ffba", // Mint RGBA + "#bae7b3", // Sea Green + "#b5d6fd", // Light Sky Blue + "#d9ecf1", // Mint Green RGBA + "#ffe6e6", // Light Pink + "#dcd0c8", // Linen + "#bed3f3", // Lavender Blush + "#ffe9f3", // Sand RGBA + "#333366", // Violet + "#993333", // Red Violet + "#3399ff", // Sky Blue ]; -let tags = [ - { name: "tag1", color: "blue" }, - { name: "tag2", color: "green" } +const random_id = () => Math.round(Date.now() * Math.random()).toString(); + +const DEFAULT_TAGS = [ + { id: random_id(), name: "Plain Text", color: TAG_COLORS[0] }, + { id: random_id(), name: "OpenAI", color: TAG_COLORS[1] }, + { id: random_id(), name: "W++", color: TAG_COLORS[2] }, + { id: random_id(), name: "Boostyle", color: TAG_COLORS[3] }, + { id: random_id(), name: "PList", color: TAG_COLORS[4] }, + { id: random_id(), name: "AliChat", color: TAG_COLORS[5] }, ]; +let tags = []; +let tag_map = {}; + +function loadTagsSettings(settings) { + tags = settings.tags !== undefined ? settings.tags : DEFAULT_TAGS; + tag_map = settings.tag_map !== undefined ? settings.tag_map : Object.create(null); +} + +function getTagsList(key) { + if (!Array.isArray(tag_map[key])) { + tag_map[key] = []; + return []; + } + + return tag_map[key].map(x => tags.find(y => y.id === x)).filter(x => x); +} + +function getTagKey() { + if (selected_group) { + return selected_group; + } + + if (this_chid) { + return characters[this_chid].avatar; + } + + return null; +} + +function addTagToMap(tagId) { + const key = getTagKey(); + + if (!key) { + return; + } + + if (!Array.isArray(tag_map[key])) { + tag_map[key] = [tagId]; + } + else { + tag_map[key].push(tagId); + } +} + +function removeTagFromMap(tagId) { + const key = getTagKey(); + + if (!key) { + return; + } + + if (!Array.isArray(tag_map[key])) { + tag_map[key] = []; + } + else { + const indexOf = tag_map[key].indexOf(tagId); + tag_map[key].splice(indexOf, 1); + } +} + +function findTag(request, resolve) { + const skipIds = [...($("#tagList").find(".tag").map((_, el) => $(el).attr("id")))]; + const haystack = tags.filter(t => !skipIds.includes(t.id)).map(t => t.name).sort(); + const needle = request.term.toLowerCase(); + const result = haystack.filter(x => x.toLowerCase().includes(needle)); + resolve(result.length !== 0 ? result : [request.term]); +} + +function selectTag(event, ui) { + let tagName = ui.item.value; + let tag = tags.find(t => t.name === tagName); + + // create new tag if it doesn't exist + if (!tag) { + tag = createNewTag(tagName); + } + + // unfocus and clear the input + $(this).val("").blur(); + + // add tag to the UI and internal map + appendTagToList("#tagList", tag, { removable: true }); + addTagToMap(tag.id); + printTags(); + saveSettingsDebounced(); + + // need to return false to keep the input clear + return false; +} + +function createNewTag(tagName) { + const tag = { + id: random_id(), + name: tagName, + color: TAG_COLORS[Math.floor(Math.random() * TAG_COLORS.length)] + }; + tags.push(tag); + return tag; +} + +function appendTagToList(listSelector, tag, { removable, editable, selectable }) { + let tagElement = $('#tag_template .tag').clone(); + tagElement.attr('id', tag.id); + tagElement.find('.tag_name').text(tag.name); + tagElement.css({ 'background-color': tag.color }); + const removeButton = tagElement.find(".tag_remove"); + removable ? removeButton.show() : removeButton.hide(); + + if (selectable) { + tagElement.on('click', onTagFilterClick); + } + + // TODO: handle color change + $(listSelector).append(tagElement); +} + +function onTagFilterClick() { + const wasSelected = $(this).hasClass('selected'); + clearTagsFilter(); + + if (wasSelected) { + return; + } + + const tagId = $(this).attr('id'); + $(this).addClass('selected'); + $('#rm_print_characters_block > div').each((_, element) => applyFilterToElement(tagId, element)); +} + +function applyFilterToElement(tagId, element) { + const isTagged = isElementTagged(element, tagId); + $(element).css('display', !isTagged ? 'none' : ''); +} + +function isElementTagged(element, tagId) { + const isGroup = $(element).hasClass('group_select'); + const isCharacter = $(element).hasClass('character_select'); + const idAttr = isGroup ? 'grid' : 'chid'; + const elementId = $(element).attr(idAttr); + const lookupValue = isCharacter ? characters[elementId].avatar : elementId; + const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId); + return isTagged; +} + +function clearTagsFilter() { + $('#rm_tag_filter .tag').removeClass('selected'); + $('#rm_print_characters_block > div').css('display', ''); +} + +function printTags() { + $('#rm_tag_filter').empty(); + const characterTagIds = Object.values(tag_map).flat(); + const tagsToDisplay = tags.filter(x => characterTagIds.includes(x.id)); + + for (const tag of tagsToDisplay) { + appendTagToList('#rm_tag_filter', tag, { removable: false, editable: false, selectable: true, }); + } +} + +function onTagRemoveClick(event) { + event.stopPropagation(); + const tag = $(this).closest(".tag"); + const tagId = tag.attr("id"); + tag.remove(); + removeTagFromMap(tagId); + printTags(); + saveSettingsDebounced(); +} + +function onTagInput(event) { + let val = $(this).val(); + if (tags.find(t => t.name === val)) return; + $(this).autocomplete("search", val); +} + +function onTagInputFocus() { + $(this).autocomplete('search', $(this).val()); +} + +$(document).on("click", ".character_select", function () { + clearTagsFilter(); + const chid = Number($(this).attr('chid')); + const key = characters[chid].avatar; + const tags = getTagsList(key); + + $("#tagList").empty(); + + for (const tag of tags) { + appendTagToList("#tagList", tag, { removable: true }); + } +}); + $(document).ready(() => { - $("#tagInput").autocomplete({ - source: tags.map(t => t.name), - select: function(event, ui) { - let tagName = ui.item.value; - let tag = tags.find(t => t.name === tagName); - if (!tag) { - tag = { - name: tagName, - color: TAG_COLORS[Math.floor(Math.random() * colors.length)] - }; - tags.push(tag); - } - let tagElement = $(`${tag.name}`); - $("#tagList").append(tagElement); - $(this).val(""); - } - }); - - $("#tagInput").on("input", function(event) { - let val = $(this).val(); - if (tags.find(t => t.name === val)) return; - $(this).autocomplete("search", val); - }); + $("#tagInput") + .autocomplete({ source: findTag, select: selectTag, minLength: 0, }) + .focus(onTagInputFocus); // <== show tag list on click + + $(document).on("click", ".tag_remove", onTagRemoveClick); + + $("#tagInput").on("input", onTagInput); }); \ No newline at end of file diff --git a/public/style.css b/public/style.css index 808168003..f2d7487d8 100644 --- a/public/style.css +++ b/public/style.css @@ -439,11 +439,6 @@ code { background-color: var(--SmartThemeEmColor); } - -#message_template { - display: none !important; -} - .auto_hide { content-visibility: auto; } @@ -2323,11 +2318,39 @@ h5 { box-sizing: border-box; border-color: var(--black30a); padding: 0.2rem 0.3rem; - font-size: calc(var(--mainFontSize) * 1.1); + font-size: calc(var(--mainFontSize) + 5%); + display: flex; + flex-direction: row; + align-items: center; + gap: 10px; + width: fit-content; } -#tagList { - margin-top: 10px; +.tag.selected { + font-weight: 500; + filter: brightness(125%); +} + +.tag_remove { + cursor: pointer; +} + +.tags { + margin: 10px 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + column-gap: 1rem; + row-gap: 0.5rem; +} + +#rm_tag_filter { + justify-content: space-evenly; +} + +#rm_tag_filter .tag { + cursor: pointer; } body .ui-front { @@ -2519,11 +2542,6 @@ body .ui-widget-content { padding: 0px; } -#group_member_template, -#group_list_template { - display: none !important; -} - .group_select { display: flex; flex-direction: row; @@ -2553,10 +2571,6 @@ body .ui-widget-content { margin-left: 5px; } -#typing_indicator_template { - display: none !important; -} - .typing_indicator { position: sticky; bottom: 10px; @@ -2600,10 +2614,6 @@ body .ui-widget-content { } } -#group_avatars_template { - display: none; -} - .avatar_collage { border-radius: 50%; position: relative; @@ -2950,6 +2960,7 @@ label[for="extensions_autoconnect"] { display: flex; align-items: center; justify-content: space-between; + gap: 10px; } .extensions_url_block h4 { @@ -3271,8 +3282,8 @@ toolcool-color-picker { width: 100%; } -#openai_logit_bias_template { - display: none; +.template_element { + display: none !important; } .openai_logit_bias_text,