mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Tag Folders: Refactor tag code into tag.js
This commit is contained in:
100
public/script.js
100
public/script.js
@@ -153,10 +153,13 @@ import {
|
|||||||
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
|
import { ModuleWorkerWrapper, doDailyExtensionUpdatesCheck, extension_settings, getContext, loadExtensionSettings, renderExtensionTemplate, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
|
||||||
import { COMMENT_NAME_DEFAULT, executeSlashCommands, getSlashCommandsHelp, processChatSlashCommands, registerSlashCommand } from './scripts/slash-commands.js';
|
import { COMMENT_NAME_DEFAULT, executeSlashCommands, getSlashCommandsHelp, processChatSlashCommands, registerSlashCommand } from './scripts/slash-commands.js';
|
||||||
import {
|
import {
|
||||||
TAG_FOLDER_DEFAULT_TYPE,
|
|
||||||
tag_map,
|
tag_map,
|
||||||
tags,
|
tags,
|
||||||
filterByTagState,
|
filterByTagState,
|
||||||
|
isBogusFolder,
|
||||||
|
isBogusFolderOpen,
|
||||||
|
chooseBogusFolder,
|
||||||
|
getTagBlock,
|
||||||
loadTagsSettings,
|
loadTagsSettings,
|
||||||
printTagFilters,
|
printTagFilters,
|
||||||
getTagsList,
|
getTagsList,
|
||||||
@@ -166,7 +169,6 @@ import {
|
|||||||
importTags,
|
importTags,
|
||||||
tag_filter_types,
|
tag_filter_types,
|
||||||
compareTagsForSort,
|
compareTagsForSort,
|
||||||
TAG_FOLDER_TYPES,
|
|
||||||
} from './scripts/tags.js';
|
} from './scripts/tags.js';
|
||||||
import {
|
import {
|
||||||
SECRET_KEYS,
|
SECRET_KEYS,
|
||||||
@@ -275,8 +277,6 @@ export {
|
|||||||
printCharacters,
|
printCharacters,
|
||||||
isOdd,
|
isOdd,
|
||||||
countOccurrences,
|
countOccurrences,
|
||||||
chooseBogusFolder,
|
|
||||||
isBogusFolder,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
showLoader();
|
showLoader();
|
||||||
@@ -1161,57 +1161,6 @@ export async function selectCharacterById(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTagBlock(item, entities) {
|
|
||||||
let count = entities.length;
|
|
||||||
|
|
||||||
const tagFolder = TAG_FOLDER_TYPES[item.folder_type];
|
|
||||||
|
|
||||||
const template = $('#bogus_folder_template .bogus_folder_select').clone();
|
|
||||||
template.addClass(tagFolder.class);
|
|
||||||
template.attr({ 'tagid': item.id, 'id': `BogusFolder${item.id}` });
|
|
||||||
template.find('.avatar').css({ 'background-color': item.color, 'color': item.color2 }).attr('title', `[Folder] ${item.name}`);
|
|
||||||
template.find('.ch_name').text(item.name).attr('title', `[Folder] ${item.name}`);
|
|
||||||
template.find('.bogus_folder_counter').text(count);
|
|
||||||
template.find('.bogus_folder_icon').addClass(tagFolder.fa_icon);
|
|
||||||
if (count == 1) {
|
|
||||||
template.find('.character_unit_name').text('character');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill inline character images
|
|
||||||
const inlineAvatars = template.find('.bogus_folder_avatars_block');
|
|
||||||
for (const entitiy of entities) {
|
|
||||||
const id = entitiy.id;
|
|
||||||
|
|
||||||
// Populate the template
|
|
||||||
const avatarTemplate = $('#bogus_folder_inline_character_template .avatar').clone();
|
|
||||||
|
|
||||||
let this_avatar = default_avatar;
|
|
||||||
if (entitiy.item.avatar != 'none') {
|
|
||||||
this_avatar = getThumbnailUrl('avatar', entitiy.item.avatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
|
|
||||||
avatarTemplate.find('img').attr('src', this_avatar).attr('alt', entitiy.item.name);
|
|
||||||
avatarTemplate.attr('title', `[Character] ${entitiy.item.name}`);
|
|
||||||
avatarTemplate.toggleClass('is_fav', entitiy.item.fav || entitiy.item.fav == 'true');
|
|
||||||
avatarTemplate.find('.ch_fav').val(entitiy.item.fav);
|
|
||||||
|
|
||||||
// If this is a group, we need to hack slightly. We still want to keep most of the css classes and layout, but use a group avatar instead.
|
|
||||||
if (entitiy.type === 'group') {
|
|
||||||
const grpTemplate = getGroupAvatar(entitiy.item);
|
|
||||||
|
|
||||||
avatarTemplate.addClass(grpTemplate.attr('class'));
|
|
||||||
avatarTemplate.empty();
|
|
||||||
avatarTemplate.append(grpTemplate.children());
|
|
||||||
avatarTemplate.attr('title', `Group: ${entitiy.item.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
inlineAvatars.append(avatarTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBackBlock() {
|
function getBackBlock() {
|
||||||
const template = $('#bogus_folder_back_template .bogus_folder_select').clone();
|
const template = $('#bogus_folder_back_template .bogus_folder_select').clone();
|
||||||
return template;
|
return template;
|
||||||
@@ -1337,26 +1286,6 @@ async function printCharacters(fullRefresh = false) {
|
|||||||
favsToHotswap();
|
favsToHotswap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether a given tag is defined as a folder. Meaning it's neither undefined nor 'NONE'.
|
|
||||||
* @returns {boolean} If it's a tag folder
|
|
||||||
*/
|
|
||||||
function isBogusFolder(tag) {
|
|
||||||
return tag?.folder_type !== undefined && tag.folder_type !== TAG_FOLDER_DEFAULT_TYPE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether a user is currently in a bogus folder.
|
|
||||||
* @returns {boolean} If currently viewing a folder
|
|
||||||
*/
|
|
||||||
function isBogusFolderOpen() {
|
|
||||||
const anyIsFolder = entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.selected
|
|
||||||
.map(tagId => tags.find(x => x.id === tagId))
|
|
||||||
.some(isBogusFolder);
|
|
||||||
|
|
||||||
return !!anyIsFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getEntitiesList({ doFilter } = {}) {
|
export function getEntitiesList({ doFilter } = {}) {
|
||||||
function characterToEntity(character, id) {
|
function characterToEntity(character, id) {
|
||||||
return { item: character, id, type: 'character' };
|
return { item: character, id, type: 'character' };
|
||||||
@@ -8099,27 +8028,6 @@ function doTogglePanels() {
|
|||||||
$('#option_settings').trigger('click');
|
$('#option_settings').trigger('click');
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseBogusFolder(source, tagId, remove = false) {
|
|
||||||
// If we are here via the 'back' action, we implicitly take the last filtered folder as one to remove
|
|
||||||
const isBack = tagId === 'back';
|
|
||||||
if (isBack) {
|
|
||||||
const drilldown = $(source).closest('#rm_characters_block').find('.rm_tag_bogus_drilldown');
|
|
||||||
const lastTag = drilldown.find('.tag:last').last();
|
|
||||||
tagId = lastTag.attr('id');
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of manually updating the filter conditions, we just "click" on the filter tag
|
|
||||||
// We search inside which filter block we are located in and use that one
|
|
||||||
const FILTER_SELECTOR = ($(source).closest('#rm_characters_block') ?? $(source).closest('#rm_group_chats_block')).find('.rm_tag_filter');
|
|
||||||
if (remove) {
|
|
||||||
// Click twice to skip over the 'excluded' state
|
|
||||||
$(FILTER_SELECTOR).find(`.tag[id=${tagId}]`).trigger('click').trigger('click');
|
|
||||||
} else {
|
|
||||||
$(FILTER_SELECTOR).find(`.tag[id=${tagId}]`).trigger('click');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDebugFunctions() {
|
function addDebugFunctions() {
|
||||||
const doBackfill = async () => {
|
const doBackfill = async () => {
|
||||||
for (const message of chat) {
|
for (const message of chat) {
|
||||||
|
@@ -7,13 +7,13 @@ import {
|
|||||||
getCharacters,
|
getCharacters,
|
||||||
entitiesFilter,
|
entitiesFilter,
|
||||||
printCharacters,
|
printCharacters,
|
||||||
chooseBogusFolder,
|
getThumbnailUrl,
|
||||||
isBogusFolder,
|
default_avatar,
|
||||||
} from '../script.js';
|
} from '../script.js';
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
import { FILTER_TYPES, FilterHelper } from './filters.js';
|
||||||
|
|
||||||
import { groupCandidatesFilter, groups, selected_group } from './group-chats.js';
|
import { groupCandidatesFilter, groups, selected_group, getGroupAvatar } from './group-chats.js';
|
||||||
import { download, onlyUnique, parseJsonFile, uuidv4, getSortableDelay } from './utils.js';
|
import { download, onlyUnique, parseJsonFile, uuidv4, getSortableDelay } from './utils.js';
|
||||||
import { power_user } from './power-user.js';
|
import { power_user } from './power-user.js';
|
||||||
|
|
||||||
@@ -23,6 +23,10 @@ export {
|
|||||||
tags,
|
tags,
|
||||||
tag_map,
|
tag_map,
|
||||||
filterByTagState,
|
filterByTagState,
|
||||||
|
isBogusFolder,
|
||||||
|
isBogusFolderOpen,
|
||||||
|
chooseBogusFolder,
|
||||||
|
getTagBlock,
|
||||||
loadTagsSettings,
|
loadTagsSettings,
|
||||||
printTagFilters,
|
printTagFilters,
|
||||||
getTagsList,
|
getTagsList,
|
||||||
@@ -142,7 +146,109 @@ function filterTagSubEntities(tag, entities) {
|
|||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether a given tag is defined as a folder. Meaning it's neither undefined nor 'NONE'.
|
||||||
|
* @returns {boolean} If it's a tag folder
|
||||||
|
*/
|
||||||
|
function isBogusFolder(tag) {
|
||||||
|
return tag?.folder_type !== undefined && tag.folder_type !== TAG_FOLDER_DEFAULT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether a user is currently in a bogus folder.
|
||||||
|
* @returns {boolean} If currently viewing a folder
|
||||||
|
*/
|
||||||
|
function isBogusFolderOpen() {
|
||||||
|
const anyIsFolder = entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.selected
|
||||||
|
.map(tagId => tags.find(x => x.id === tagId))
|
||||||
|
.some(isBogusFolder);
|
||||||
|
|
||||||
|
return !!anyIsFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to be called when a specific tag/folder is chosen to "drill down".
|
||||||
|
* @param {*} source The jQuery element clicked when choosing the folder
|
||||||
|
* @param {string} tagId The tag id that is behind the chosen folder
|
||||||
|
* @param {boolean} remove Whether the given tag should be removed (otherwise it is added/chosen)
|
||||||
|
*/
|
||||||
|
function chooseBogusFolder(source, tagId, remove = false) {
|
||||||
|
// If we are here via the 'back' action, we implicitly take the last filtered folder as one to remove
|
||||||
|
const isBack = tagId === 'back';
|
||||||
|
if (isBack) {
|
||||||
|
const drilldown = $(source).closest('#rm_characters_block').find('.rm_tag_bogus_drilldown');
|
||||||
|
const lastTag = drilldown.find('.tag:last').last();
|
||||||
|
tagId = lastTag.attr('id');
|
||||||
|
remove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instead of manually updating the filter conditions, we just "click" on the filter tag
|
||||||
|
// We search inside which filter block we are located in and use that one
|
||||||
|
const FILTER_SELECTOR = ($(source).closest('#rm_characters_block') ?? $(source).closest('#rm_group_chats_block')).find('.rm_tag_filter');
|
||||||
|
if (remove) {
|
||||||
|
// Click twice to skip over the 'excluded' state
|
||||||
|
$(FILTER_SELECTOR).find(`.tag[id=${tagId}]`).trigger('click').trigger('click');
|
||||||
|
} else {
|
||||||
|
$(FILTER_SELECTOR).find(`.tag[id=${tagId}]`).trigger('click');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTagBlock(item, entities) {
|
||||||
|
let count = entities.length;
|
||||||
|
|
||||||
|
const tagFolder = TAG_FOLDER_TYPES[item.folder_type];
|
||||||
|
|
||||||
|
const template = $('#bogus_folder_template .bogus_folder_select').clone();
|
||||||
|
template.addClass(tagFolder.class);
|
||||||
|
template.attr({ 'tagid': item.id, 'id': `BogusFolder${item.id}` });
|
||||||
|
template.find('.avatar').css({ 'background-color': item.color, 'color': item.color2 }).attr('title', `[Folder] ${item.name}`);
|
||||||
|
template.find('.ch_name').text(item.name).attr('title', `[Folder] ${item.name}`);
|
||||||
|
template.find('.bogus_folder_counter').text(count);
|
||||||
|
template.find('.bogus_folder_icon').addClass(tagFolder.fa_icon);
|
||||||
|
if (count == 1) {
|
||||||
|
template.find('.character_unit_name').text('character');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill inline character images
|
||||||
|
buildTagInlineAvatars(template, entities);
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTagInlineAvatars(template, entities) {
|
||||||
|
const inlineAvatars = template.find('.bogus_folder_avatars_block');
|
||||||
|
inlineAvatars.empty();
|
||||||
|
|
||||||
|
for (const entitiy of entities) {
|
||||||
|
const id = entitiy.id;
|
||||||
|
|
||||||
|
// Populate the template
|
||||||
|
const avatarTemplate = $('#bogus_folder_inline_character_template .avatar').clone();
|
||||||
|
|
||||||
|
let this_avatar = default_avatar;
|
||||||
|
if (entitiy.item.avatar !== undefined && entitiy.item.avatar != 'none') {
|
||||||
|
this_avatar = getThumbnailUrl('avatar', entitiy.item.avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
|
||||||
|
avatarTemplate.find('img').attr('src', this_avatar).attr('alt', entitiy.item.name);
|
||||||
|
avatarTemplate.attr('title', `[Character] ${entitiy.item.name}`);
|
||||||
|
avatarTemplate.toggleClass('is_fav', entitiy.item.fav || entitiy.item.fav == 'true');
|
||||||
|
avatarTemplate.find('.ch_fav').val(entitiy.item.fav);
|
||||||
|
|
||||||
|
// If this is a group, we need to hack slightly. We still want to keep most of the css classes and layout, but use a group avatar instead.
|
||||||
|
if (entitiy.type === 'group') {
|
||||||
|
const grpTemplate = getGroupAvatar(entitiy.item);
|
||||||
|
|
||||||
|
avatarTemplate.addClass(grpTemplate.attr('class'));
|
||||||
|
avatarTemplate.empty();
|
||||||
|
avatarTemplate.append(grpTemplate.children());
|
||||||
|
avatarTemplate.attr('title', `Group: ${entitiy.item.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
inlineAvatars.append(avatarTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the favorite filter to the character list.
|
* Applies the favorite filter to the character list.
|
||||||
|
Reference in New Issue
Block a user