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);
}