From cb1e254cb91cbb2b204ea32546e5458b10b508a7 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Fri, 10 Nov 2023 21:56:25 +0200 Subject: [PATCH] Add tags as folders display mode --- public/css/st-tailwind.css | 6 +- public/index.html | 20 +++++ public/script.js | 43 +++++++++++ public/scripts/filters.js | 6 +- public/scripts/power-user.js | 40 +++++++++- public/scripts/tags.js | 137 +++++++++++++++++++++-------------- public/scripts/utils.js | 2 +- public/style.css | 27 +++++++ 8 files changed, 219 insertions(+), 62 deletions(-) diff --git a/public/css/st-tailwind.css b/public/css/st-tailwind.css index 3b717c18d..701d03890 100644 --- a/public/css/st-tailwind.css +++ b/public/css/st-tailwind.css @@ -167,6 +167,10 @@ box-shadow: none !important; } +.height100p { + height: 100%; +} + .height100pSpaceEvenly { align-content: space-evenly; height: 100%; @@ -514,4 +518,4 @@ textarea:disabled { height: 30px; text-align: center; padding: 5px; -} \ No newline at end of file +} diff --git a/public/index.html b/public/index.html index 1d98a6f46..9c0f396cd 100644 --- a/public/index.html +++ b/public/index.html @@ -2731,6 +2731,10 @@ Characters Hotswap +

Miscellaneous

@@ -4160,6 +4164,22 @@
+
+
+
+ +
+
+
+ +
+
+ + character card(s) +
+
+
+
diff --git a/public/script.js b/public/script.js index ff7340566..e417000be 100644 --- a/public/script.js +++ b/public/script.js @@ -989,6 +989,33 @@ export async function selectCharacterById(id) { } } +function getTagBlock(item) { + const count = Object.values(tag_map).flat().filter(x => x == item.id).length; + const template = $('#bogus_folder_template .bogus_folder_select').clone(); + template.attr({ 'tagid': item.id, 'id': `BogusFolder${item.id}` }); + template.find('.avatar').css({'background-color': item.color, 'color': item.color2 }); + template.find('.ch_name').text(item.name); + template.find('.bogus_folder_counter').text(count); + template.on('click', () => { + console.log('Bogus folder clicked', item.id); + entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [item.id], bogus: true, }); + }); + return template; +} + +function getEmptyBlock() { + const icons = ['fa-dragon', 'fa-otter', 'fa-kiwi-bird', 'fa-crow', 'fa-frog']; + const texts = ['Here be dragons', 'Otterly empty', 'Kiwibunga', 'Pump-a-Rum', 'Croak it']; + const roll = Math.floor(Math.random() * icons.length); + const emptyBlock = ` +
+ +

${texts[roll]}

+

There are no items to display.

+
`; + return $(emptyBlock); +} + function getCharacterBlock(item, id) { let this_avatar = default_avatar; if (item.avatar != "none") { @@ -1037,6 +1064,12 @@ async function printCharacters(fullRefresh = false) { saveCharactersPage = 0; printTagFilters(tag_filter_types.character); printTagFilters(tag_filter_types.group_member); + const isBogusFolderOpen = !!entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.bogus; + + // Return to main list + if (isBogusFolderOpen) { + entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [] }); + } await delay(1); displayOverrideWarnings(); @@ -1065,6 +1098,12 @@ async function printCharacters(fullRefresh = false) { if (i.type === 'group') { $("#rm_print_characters_block").append(getGroupBlock(i.item)); } + if (i.type === 'tag') { + $("#rm_print_characters_block").append(getTagBlock(i.item)); + } + } + if (!data.length) { + $("#rm_print_characters_block").append(getEmptyBlock()); } eventSource.emit(event_types.CHARACTER_PAGE_LOADED); }, @@ -1087,6 +1126,10 @@ export function getEntitiesList({ doFilter } = {}) { entities.push(...characters.map((item, index) => ({ item, id: index, type: 'character' }))); entities.push(...groups.map((item) => ({ item, id: item.id, type: 'group' }))); + if (power_user.bogus_folders) { + entities.push(...tags.map((item) => ({ item, id: item.id, type: 'tag' }))); + } + if (doFilter) { entities = entitiesFilter.applyFilters(entities); } diff --git a/public/scripts/filters.js b/public/scripts/filters.js index 364259687..f382845cc 100644 --- a/public/scripts/filters.js +++ b/public/scripts/filters.js @@ -1,4 +1,4 @@ -import { fuzzySearchCharacters, fuzzySearchGroups, fuzzySearchWorldInfo, power_user } from "./power-user.js"; +import { fuzzySearchCharacters, fuzzySearchGroups, fuzzySearchTags, fuzzySearchWorldInfo, power_user } from "./power-user.js"; import { tag_map } from "./tags.js"; /** @@ -148,16 +148,20 @@ export class FilterHelper { const searchValue = this.filterData[FILTER_TYPES.SEARCH].trim().toLowerCase(); const fuzzySearchCharactersResults = power_user.fuzzy_search ? fuzzySearchCharacters(searchValue) : []; const fuzzySearchGroupsResults = power_user.fuzzy_search ? fuzzySearchGroups(searchValue) : []; + const fuzzySearchTagsResult = power_user.fuzzy_search ? fuzzySearchTags(searchValue) : []; function getIsValidSearch(entity) { const isGroup = entity.type === 'group'; const isCharacter = entity.type === 'character'; + const isTag = entity.type === 'tag'; if (power_user.fuzzy_search) { if (isCharacter) { return fuzzySearchCharactersResults.includes(parseInt(entity.id)); } else if (isGroup) { return fuzzySearchGroupsResults.includes(String(entity.id)); + } else if (isTag) { + return fuzzySearchTagsResult.includes(String(entity.id)); } else { return false; } diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 4716f29df..efc4c9e14 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -31,6 +31,7 @@ import { } from "./instruct-mode.js"; import { registerSlashCommand } from "./slash-commands.js"; +import { tags } from "./tags.js"; import { tokenizers } from "./tokenizers.js"; import { countOccurrences, debounce, delay, isOdd, resetScrollHeight, sortMoments, stringToRange, timestampToMoment } from "./utils.js"; @@ -218,6 +219,7 @@ let power_user = { fuzzy_search: false, encode_tags: false, servers: [], + bogus_folders: false, }; let themes = []; @@ -486,7 +488,7 @@ async function switchZenSliders() { $("#clickSlidersTips").hide() $("#pro-settings-block input[type='number']").hide(); //hide number inputs that are not 'seed' inputs - $(`#textgenerationwebui_api-settings :input[type='number']:not([id^='seed']), + $(`#textgenerationwebui_api-settings :input[type='number']:not([id^='seed']), #kobold_api-settings :input[type='number']:not([id^='seed'])`).hide() //hide original sliders $(`#textgenerationwebui_api-settings input[type='range'], @@ -1038,7 +1040,14 @@ async function applyTheme(name) { localStorage.setItem(storage_keys.hotswap_enabled, Boolean(power_user.hotswap_enabled)); switchHotswap(); } - } + }, + { + key: 'bogus_folders', + action: async () => { + $('#bogus_folders').prop('checked', power_user.bogus_folders); + await printCharacters(true); + }, + }, ]; for (const { key, selector, type, action } of themeProperties) { @@ -1203,6 +1212,7 @@ function loadPowerUserSettings(settings, data) { $("#console_log_prompts").prop("checked", power_user.console_log_prompts); $('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown); $('#auto_scroll_chat_to_bottom').prop("checked", power_user.auto_scroll_chat_to_bottom); + $('#bogus_folders').prop("checked", power_user.bogus_folders); $(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true); $(`#send_on_enter option[value=${power_user.send_on_enter}]`).attr("selected", true); $("#import_card_tags").prop("checked", power_user.import_card_tags); @@ -1537,6 +1547,22 @@ export function fuzzySearchWorldInfo(data, searchValue) { return results.map(x => x.item?.uid); } +export function fuzzySearchTags(searchValue) { + const fuse = new Fuse(tags, { + keys: [ + { name: 'name', weight: 1}, + ], + includeScore: true, + ignoreLocation: true, + threshold: 0.2 + }); + + const results = fuse.search(searchValue); + console.debug('Tags fuzzy search results for ' + searchValue, results); + const ids = results.map(x => String(x.item?.id)).filter(x => x); + return ids; +} + export function fuzzySearchGroups(searchValue) { const fuse = new Fuse(groups, { keys: [ @@ -1664,8 +1690,7 @@ async function saveTheme() { enableLabMode: power_user.enableLabMode, hotswap_enabled: power_user.hotswap_enabled, custom_css: power_user.custom_css, - - + bogus_folders: power_user.bogus_folders, }; const response = await fetch('/savetheme', { @@ -2790,6 +2815,13 @@ $(document).ready(() => { switchSimpleMode(); }); + $('#bogus_folders').on('input', function() { + const value = !!$(this).prop('checked'); + power_user.bogus_folders = value; + saveSettingsDebounced(); + printCharacters(true); + }); + $(document).on('click', '#debug_table [data-debug-function]', function () { const functionId = $(this).data('debug-function'); const functionRecord = debug_functions.find(f => f.functionId === functionId); diff --git a/public/scripts/tags.js b/public/scripts/tags.js index b8bc89abb..02425f644 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -6,6 +6,7 @@ import { menu_type, getCharacters, entitiesFilter, + printCharacters, } from "../script.js"; import { FILTER_TYPES, FilterHelper } from "./filters.js"; @@ -48,12 +49,12 @@ const InListActionable = { } const DEFAULT_TAGS = [ - { id: uuidv4(), name: "Plain Text" }, - { id: uuidv4(), name: "OpenAI" }, - { id: uuidv4(), name: "W++" }, - { id: uuidv4(), name: "Boostyle" }, - { id: uuidv4(), name: "PList" }, - { id: uuidv4(), name: "AliChat" }, + { id: uuidv4(), name: "Plain Text", create_date: Date.now() }, + { id: uuidv4(), name: "OpenAI", create_date: Date.now() }, + { id: uuidv4(), name: "W++", create_date: Date.now() }, + { id: uuidv4(), name: "Boostyle", create_date: Date.now() }, + { id: uuidv4(), name: "PList", create_date: Date.now() }, + { id: uuidv4(), name: "AliChat", create_date: Date.now() }, ]; let tags = []; @@ -208,8 +209,8 @@ function selectTag(event, ui, listSelector) { const characterIds = characterData ? JSON.parse(characterData).characterIds : null; if (characterIds) { - characterIds.forEach((characterId) => addTagToMap(tag.id,characterId)); - } else { + characterIds.forEach((characterId) => addTagToMap(tag.id, characterId)); + } else { addTagToMap(tag.id); } @@ -232,7 +233,6 @@ function getExistingTags(new_tags) { return existing_tags } - async function importTags(imported_char) { let imported_tags = imported_char.tags.filter(t => t !== "ROOT" && t !== "TAVERN"); let existingTags = await getExistingTags(imported_tags); @@ -272,13 +272,13 @@ async function importTags(imported_char) { return false; } - function createNewTag(tagName) { const tag = { id: uuidv4(), name: tagName, color: '', color2: '', + create_date: Date.now(), }; tags.push(tag); return tag; @@ -477,55 +477,78 @@ export function createTagInput(inputSelector, listSelector) { function onViewTagsListClick() { $('#dialogue_popup').addClass('large_dialogue_popup'); - const list = document.createElement('div'); + const list = $(document.createElement('div')); + list.attr('id', 'tag_view_list'); const everything = Object.values(tag_map).flat(); - $(list).append('

Tags

Click on the tag name to edit it.
'); - $(list).append('Click on color box to assign new color.

'); + $(list).append(` +
+

Tag Management

+ +
+
+ + Click on the tag name to edit it.
+ Click on color box to assign new color. +
+
`); for (const tag of tags.slice().sort((a, b) => a?.name?.toLowerCase()?.localeCompare(b?.name?.toLowerCase()))) { - const count = everything.filter(x => x == tag.id).length; - const template = $('#tag_view_template .tag_view_item').clone(); - template.attr('id', tag.id); - template.find('.tag_view_counter_value').text(count); - template.find('.tag_view_name').text(tag.name); - template.find('.tag_view_name').addClass('tag'); - - template.find('.tag_view_name').css('background-color', tag.color); - template.find('.tag_view_name').css('color', tag.color2); - - const colorPickerId = tag.id + "-tag-color"; - const colorPicker2Id = tag.id + "-tag-color2"; - - template.find('.tagColorPickerHolder').html( - `` - ); - template.find('.tagColorPicker2Holder').html( - `` - ); - - template.find('.tag-color').attr('id', colorPickerId); - template.find('.tag-color2').attr('id', colorPicker2Id); - - list.appendChild(template.get(0)); - - setTimeout(function () { - document.querySelector(`.tag-color[id="${colorPickerId}"`).addEventListener('change', (evt) => { - onTagColorize(evt); - }); - - }, 100); - - setTimeout(function () { - document.querySelector(`.tag-color2[id="${colorPicker2Id}"`).addEventListener('change', (evt) => { - onTagColorize2(evt); - }); - }, 100); - - $(colorPickerId).color = tag.color; - $(colorPicker2Id).color = tag.color2; - + appendViewTagToList(list, tag, everything); } - callPopup(list.outerHTML, 'text'); + + callPopup(list, 'text'); +} + +function onTagCreateClick() { + const tag = createNewTag('New Tag'); + appendViewTagToList($('#tag_view_list'), tag, []); + printCharacters(false); + saveSettingsDebounced(); +} + +function appendViewTagToList(list, tag, everything) { + const count = everything.filter(x => x == tag.id).length; + const template = $('#tag_view_template .tag_view_item').clone(); + template.attr('id', tag.id); + template.find('.tag_view_counter_value').text(count); + template.find('.tag_view_name').text(tag.name); + template.find('.tag_view_name').addClass('tag'); + + template.find('.tag_view_name').css('background-color', tag.color); + template.find('.tag_view_name').css('color', tag.color2); + + const colorPickerId = tag.id + "-tag-color"; + const colorPicker2Id = tag.id + "-tag-color2"; + + template.find('.tagColorPickerHolder').html( + `` + ); + template.find('.tagColorPicker2Holder').html( + `` + ); + + template.find('.tag-color').attr('id', colorPickerId); + template.find('.tag-color2').attr('id', colorPicker2Id); + + list.append(template); + + setTimeout(function () { + document.querySelector(`.tag-color[id="${colorPickerId}"`).addEventListener('change', (evt) => { + onTagColorize(evt); + }); + }, 100); + + setTimeout(function () { + document.querySelector(`.tag-color2[id="${colorPicker2Id}"`).addEventListener('change', (evt) => { + onTagColorize2(evt); + }); + }, 100); + + $(colorPickerId).color = tag.color; + $(colorPicker2Id).color = tag.color2; } function onTagDeleteClick() { @@ -541,6 +564,7 @@ function onTagDeleteClick() { tags.splice(index, 1); $(`.tag[id="${id}"]`).remove(); $(`.tag_view_item[id="${id}"]`).remove(); + printCharacters(false); saveSettingsDebounced(); } @@ -559,6 +583,7 @@ function onTagColorize(evt) { const newColor = evt.detail.rgba; $(evt.target).parent().parent().find('.tag_view_name').css('background-color', newColor); $(`.tag[id="${id}"]`).css('background-color', newColor); + $(`.bogus_folder_select[tagid="${id}"] .avatar`).css('background-color', newColor); const tag = tags.find(x => x.id === id); tag.color = newColor; console.debug(tag); @@ -571,6 +596,7 @@ function onTagColorize2(evt) { const newColor = evt.detail.rgba; $(evt.target).parent().parent().find('.tag_view_name').css('color', newColor); $(`.tag[id="${id}"]`).css('color', newColor); + $(`.bogus_folder_select[tagid="${id}"] .avatar`).css('color', newColor); const tag = tags.find(x => x.id === id); tag.color2 = newColor; console.debug(tag); @@ -597,4 +623,5 @@ $(document).ready(() => { $(document).on("click", ".tags_view", onViewTagsListClick); $(document).on("click", ".tag_delete", onTagDeleteClick); $(document).on("input", ".tag_view_name", onTagRenameInput); + $(document).on("click", ".tag_view_create", onTagCreateClick); }); diff --git a/public/scripts/utils.js b/public/scripts/utils.js index da53df45e..251bca0c9 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -550,7 +550,7 @@ export function timestampToMoment(timestamp) { return moment.invalid(); } - // Unix time (legacy TAI) + // Unix time (legacy TAI / tags) if (typeof timestamp === 'number') { return moment(timestamp); } diff --git a/public/style.css b/public/style.css index d3468a50a..e63db8100 100644 --- a/public/style.css +++ b/public/style.css @@ -889,10 +889,20 @@ hr { box-shadow: 0 0 5px var(--black50a); } +.bogus_folder_select .avatar, .character_select .avatar { flex: unset; } +.bogus_folder_select .avatar { + justify-content: center; + background-color: var(--SmartThemeBlurTintColor); + color: var(--SmartThemeBodyColor); + outline-style: solid; + outline-width: 1px; + outline-color: var(--SmartThemeBorderColor); +} + .mes_block { padding-top: 0; padding-left: 10px; @@ -1213,6 +1223,20 @@ input[type="file"] { width: calc(100% - 85px); } +#rm_print_characters_block .empty_block { + display: flex; + flex-direction: column; + gap: 10px; + flex-wrap: wrap; + text-align: center; + height: 100%; + width: 100%; + opacity: 0.5; + justify-content: center; + margin: 0 auto; + align-items: center; +} + #rm_print_characters_block { overflow-y: auto; flex-grow: 1; @@ -1479,6 +1503,7 @@ input[type=search]:focus::-webkit-search-cancel-button { pointer-events: all; } +.bogus_folder_select, .character_select { display: flex; flex-direction: row; @@ -1505,6 +1530,7 @@ input[type=search]:focus::-webkit-search-cancel-button { font-style: italic; } +.bogus_folder_select .avatar, .character_select .avatar { align-self: center; } @@ -1532,6 +1558,7 @@ input[type=search]:focus::-webkit-search-cancel-button { display: block; } +.bogus_folder_select:hover, .character_select:hover { background-color: var(--white30a); }