Tag Folders: Rework favorites display

- Favorites display uses same method than inline avatars
- Favorites now respect avatar style
This commit is contained in:
Wolfsblvt 2024-03-06 04:59:39 +01:00
parent 8e184254c8
commit fc6146fa00
6 changed files with 110 additions and 195 deletions

View File

@ -24,9 +24,7 @@ body.no-modelIcons .icon-svg {
} }
body.square-avatars .avatar, body.square-avatars .avatar,
body.square-avatars .avatar img, body.square-avatars .avatar img {
body.square-avatars .hotswapAvatar,
body.square-avatars .hotswapAvatar img {
border-radius: var(--avatar-base-border-radius) !important; border-radius: var(--avatar-base-border-radius) !important;
} }
@ -113,10 +111,6 @@ body.big-avatars .bogus_folder_select .avatar {
flex: unset; flex: unset;
} }
body:not(.big-avatars) .avatar {
border-radius: var(--avatar-base-border-radius-round);
}
body.big-avatars .avatar { body.big-avatars .avatar {
width: calc(var(--avatar-base-height) * var(--big-avatar-width-factor)); width: calc(var(--avatar-base-height) * var(--big-avatar-width-factor));
height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor)); height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor));
@ -129,7 +123,6 @@ body.big-avatars .avatar {
/* align-self: unset; */ /* align-self: unset; */
overflow: visible; overflow: visible;
border-radius: calc(var(--avatar-base-border-radius) * var(--big-avatar-border-factor)); border-radius: calc(var(--avatar-base-border-radius) * var(--big-avatar-border-factor));
flex: 1
} }
body.big-avatars #user_avatar_block .avatar, body.big-avatars #user_avatar_block .avatar,
@ -177,30 +170,27 @@ body.big-avatars .avatar-container .ch_description {
text-overflow: unset; text-overflow: unset;
} }
body.big-avatars .avatars_inline .avatar, body.big-avatars .avatars_inline_small .avatar,
body.big-avatars .avatars_inline .avatar img { body.big-avatars .avatars_inline_small .avatar img {
width: calc(var(--avatar-base-width) * var(--big-avatar-width-factor) * var(--inline-avatar-factor)); width: calc(var(--avatar-base-width) * var(--big-avatar-width-factor) * var(--inline-avatar-small-factor));
height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-factor)); height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-small-factor));
} }
body.big-avatars .bogus_folder_avatars_block { body.big-avatars .avatars_inline {
flex-wrap: wrap; max-height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) + 2 * var(--avatar-base-border-radius));
overflow: hidden;
height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-factor) + 2 * var(--avatar-base-border-radius));
/* margin-top: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-factor) * 0.2); */
} }
body.big-avatars .bogus_folder_avatars_block .avatar { body.big-avatars .avatars_inline.avatars_inline_small {
margin: calc(var(--avatar-base-border-radius)) 0; height: calc(var(--avatar-base-height) * var(--big-avatar-height-factor) * var(--inline-avatar-small-factor) + 2 * var(--avatar-base-border-radius));
} }
body:not(.big-avatars) .avatars_inline .avatar_collage { body:not(.big-avatars) .avatars_inline_small .avatar_collage {
min-width: calc(var(--avatar-base-width) * var(--inline-avatar-factor)); min-width: calc(var(--avatar-base-width) * var(--inline-avatar-small-factor));
} }
body.big-avatars .avatars_inline .avatar_collage { body.big-avatars .avatars_inline_small .avatar_collage {
min-width: calc(var(--avatar-base-width) * var(--big-avatar-width-factor) * var(--inline-avatar-factor)); min-width: calc(var(--avatar-base-width) * var(--big-avatar-width-factor) * var(--inline-avatar-small-factor));
max-width: calc(var(--avatar-base-width) * var(--big-avatar-width-factor) * var(--inline-avatar-factor)); max-width: calc(var(--avatar-base-width) * var(--big-avatar-width-factor) * var(--inline-avatar-small-factor));
} }
/* border radius for big avatars collages */ /* border radius for big avatars collages */

View File

@ -3888,7 +3888,7 @@
<div class="right_menu_button fa-solid fa-list-ul" id="rm_button_characters" title="Select/Create Characters" data-i18n="[title]Select/Create Characters"></div> <div class="right_menu_button fa-solid fa-list-ul" id="rm_button_characters" title="Select/Create Characters" data-i18n="[title]Select/Create Characters"></div>
</div> </div>
<div id="HotSwapWrapper" class="alignitemscenter flex-container margin0auto width100p"> <div id="HotSwapWrapper" class="alignitemscenter flex-container margin0auto width100p">
<div class="hotswap flex-container flex1"></div> <div class="hotswap avatars_inline flex-container"></div>
</div> </div>
</div> </div>
<hr> <hr>
@ -5022,15 +5022,10 @@
<small class="character_version bogus_folder_counter"></small> <small class="character_version bogus_folder_counter"></small>
<small class="bogus_folder_unit character_unit_name" data="characters">characters</small> <small class="bogus_folder_unit character_unit_name" data="characters">characters</small>
</div> </div>
<div class="bogus_folder_avatars_block avatars_inline tags tags_inline"></div> <div class="bogus_folder_avatars_block avatars_inline avatars_inline_small tags tags_inline"></div>
</div> </div>
</div> </div>
</div> </div>
<div id="bogus_folder_inline_character_template" class="template_element">
<div class="avatar flex alignitemscenter textAlignCenter">
<img src="">
</div>
</div>
<div id="bogus_folder_back_template" class="template_element"> <div id="bogus_folder_back_template" class="template_element">
<div class="bogus_folder_select flex-container wide100p alignitemsflexstart" id="BogusFolderBack" tagid="back"> <div class="bogus_folder_select flex-container wide100p alignitemsflexstart" id="BogusFolderBack" tagid="back">
<div class="avatar flex alignitemscenter textAlignCenter"> <div class="avatar flex alignitemscenter textAlignCenter">
@ -5043,9 +5038,9 @@
</div> </div>
</div> </div>
</div> </div>
<div id="hotswap_template" class="template_element"> <div id="inline_avatar_template" class="template_element">
<div class="hotswapAvatar" title="Add a character/group to favorites to display it here!"> <div class="avatar inline_avatar flex alignitemscenter textAlignCenter">
<img src="/img/ai4.png"> <img src="">
</div> </div>
</div> </div>
<div id="alternate_greetings_template" class="template_element"> <div id="alternate_greetings_template" class="template_element">

View File

@ -249,6 +249,7 @@ export {
scrollChatToBottom, scrollChatToBottom,
isStreamingEnabled, isStreamingEnabled,
getThumbnailUrl, getThumbnailUrl,
buildAvatarList,
getStoppingStrings, getStoppingStrings,
reloadMarkdownProcessor, reloadMarkdownProcessor,
getCurrentChatId, getCurrentChatId,
@ -1289,7 +1290,7 @@ async function printCharacters(fullRefresh = false) {
favsToHotswap(); favsToHotswap();
} }
export function getEntitiesList({ doFilter } = {}) { export function getEntitiesList({ doFilter = false, doSort = true } = {}) {
function characterToEntity(character, id) { function characterToEntity(character, id) {
return { item: character, id, type: 'character' }; return { item: character, id, type: 'character' };
} }
@ -1327,7 +1328,9 @@ export function getEntitiesList({ doFilter } = {}) {
entities = filterByTagState(entities, { globalDisplayFilters: true }); entities = filterByTagState(entities, { globalDisplayFilters: true });
} }
sortEntitiesList(entities); if (doSort) {
sortEntitiesList(entities);
}
return entities; return entities;
} }
@ -5245,6 +5248,49 @@ function getThumbnailUrl(type, file) {
return `/thumbnail?type=${type}&file=${encodeURIComponent(file)}`; return `/thumbnail?type=${type}&file=${encodeURIComponent(file)}`;
} }
function buildAvatarList(block, entities, { templateId = 'inline_avatar_template', empty = true, selectable = false } = {}) {
if (empty) {
block.empty();
}
for (const entity of entities) {
const id = entity.id;
// Populate the template
const avatarTemplate = $(`#${templateId} .avatar`).clone();
let this_avatar = default_avatar;
if (entity.item.avatar !== undefined && entity.item.avatar != 'none') {
this_avatar = getThumbnailUrl('avatar', entity.item.avatar);
}
avatarTemplate.attr('data-type', entity.type);
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
avatarTemplate.find('img').attr('src', this_avatar).attr('alt', entity.item.name);
avatarTemplate.attr('title', `[Character] ${entity.item.name}`);
avatarTemplate.toggleClass('is_fav', entity.item.fav || entity.item.fav == 'true');
avatarTemplate.find('.ch_fav').val(entity.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 (entity.type === 'group') {
const grpTemplate = getGroupAvatar(entity.item);
avatarTemplate.addClass(grpTemplate.attr('class'));
avatarTemplate.empty();
avatarTemplate.append(grpTemplate.children());
avatarTemplate.attr('title', `[Group] ${entity.item.name}`);
}
if (selectable) {
avatarTemplate.addClass('selectable');
avatarTemplate.toggleClass('character_select', entity.type === 'character');
avatarTemplate.toggleClass('group_select', entity.type === 'group');
}
block.append(avatarTemplate);
}
}
async function getChat() { async function getChat() {
//console.log('/api/chats/get -- entered for -- ' + characters[this_chid].name); //console.log('/api/chats/get -- entered for -- ' + characters[this_chid].name);
try { try {

View File

@ -12,6 +12,7 @@ import {
setActiveCharacter, setActiveCharacter,
getEntitiesList, getEntitiesList,
getThumbnailUrl, getThumbnailUrl,
buildAvatarList,
selectCharacterById, selectCharacterById,
eventSource, eventSource,
menu_type, menu_type,
@ -264,82 +265,16 @@ async function RA_autoloadchat() {
export async function favsToHotswap() { export async function favsToHotswap() {
const entities = getEntitiesList({ doFilter: false }); const entities = getEntitiesList({ doFilter: false });
const container = $('#right-nav-panel .hotswap'); const container = $('#right-nav-panel .hotswap');
const template = $('#hotswap_template .hotswapAvatar');
const DEFAULT_COUNT = 6;
const WIDTH_PER_ITEM = 60; // 50px + 5px gap + 5px padding
const containerWidth = container.outerWidth();
const maxCount = containerWidth > 0 ? Math.floor(containerWidth / WIDTH_PER_ITEM) : DEFAULT_COUNT;
let count = 0;
const promises = []; const favs = entities.filter(x => x.item.fav || x.item.fav == 'true');
const newContainer = container.clone();
newContainer.empty();
for (const entity of entities) {
if (count >= maxCount) {
break;
}
const isFavorite = entity.item.fav || entity.item.fav == 'true';
if (!isFavorite) {
continue;
}
const isCharacter = entity.type === 'character';
const isGroup = entity.type === 'group';
const grid = isGroup ? entity.id : '';
const chid = isCharacter ? entity.id : '';
let slot = template.clone();
slot.toggleClass('character_select', isCharacter);
slot.toggleClass('group_select', isGroup);
slot.attr('grid', isGroup ? grid : '');
slot.attr('chid', isCharacter ? chid : '');
slot.data('id', isGroup ? grid : chid);
if (isGroup) {
const group = groups.find(x => x.id === grid);
const avatar = getGroupAvatar(group);
$(slot).find('img').replaceWith(avatar);
$(slot).attr('title', group.name);
}
if (isCharacter) {
const imgLoadPromise = new Promise((resolve) => {
const avatarUrl = getThumbnailUrl('avatar', entity.item.avatar);
$(slot).find('img').attr('src', avatarUrl).on('load', resolve);
$(slot).attr('title', entity.item.avatar);
});
// if the image doesn't load in 500ms, resolve the promise anyway
promises.push(Promise.race([imgLoadPromise, delay(500)]));
}
$(slot).css('cursor', 'pointer');
newContainer.append(slot);
count++;
}
// don't fill leftover spaces with avatar placeholders
// just evenly space the selected avatars instead
/*
if (count < maxCount) { //if any space is left over
let leftOverSlots = maxCount - count;
for (let i = 1; i <= leftOverSlots; i++) {
newContainer.append(template.clone());
}
}
*/
await Promise.allSettled(promises);
//helpful instruction message if no characters are favorited //helpful instruction message if no characters are favorited
if (count === 0) { container.html('<small><span><i class="fa-solid fa-star"></i> Favorite characters to add them to HotSwaps</span></small>'); } if (favs.length == 0) {
//otherwise replace with fav'd characters container.html('<small><span><i class="fa-solid fa-star"></i> Favorite characters to add them to HotSwaps</span></small>');
if (count > 0) { return;
container.replaceWith(newContainer);
} }
buildAvatarList(container, favs, { selectable: true });
} }
//changes input bar and send button display depending on connection status //changes input bar and send button display depending on connection status

View File

@ -7,13 +7,12 @@ import {
getCharacters, getCharacters,
entitiesFilter, entitiesFilter,
printCharacters, printCharacters,
getThumbnailUrl, buildAvatarList,
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, getGroupAvatar } from './group-chats.js'; import { groupCandidatesFilter, groups, selected_group } 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';
@ -210,46 +209,11 @@ function getTagBlock(item, entities) {
} }
// Fill inline character images // Fill inline character images
buildTagInlineAvatars(template, entities); buildAvatarList(template.find('.bogus_folder_avatars_block'), entities);
return template; 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.
* @param {FilterHelper} filterHelper Instance of FilterHelper class. * @param {FilterHelper} filterHelper Instance of FilterHelper class.

View File

@ -101,7 +101,7 @@
--avatar-base-width: 50px; --avatar-base-width: 50px;
--avatar-base-border-radius: 2px; --avatar-base-border-radius: 2px;
--avatar-base-border-radius-round: 50%; --avatar-base-border-radius-round: 50%;
--inline-avatar-factor: 0.6; --inline-avatar-small-factor: 0.6;
} }
* { * {
@ -868,8 +868,10 @@ hr {
.avatar { .avatar {
width: var(--avatar-base-width); width: var(--avatar-base-width);
height: var(--avatar-base-height); height: var(--avatar-base-height);
border-radius: var(--avatar-base-border-radius-round);
border-style: none; border-style: none;
flex: 1; flex: 1;
transition: 250ms;
} }
.last_mes .mesAvatarWrapper { .last_mes .mesAvatarWrapper {
@ -880,40 +882,21 @@ hr {
cursor: pointer; cursor: pointer;
} }
#HotSwapWrapper .hotswap { .hotswap {
justify-content: space-evenly; margin: 5px;
} }
.hotswapAvatar, .avatar.selectable {
.hotswapAvatar .avatar { opacity: 0.6;
width: var(--avatar-base-width) !important;
height: var(--avatar-base-height) !important;
border-style: none;
} }
.hotswapAvatar { .avatar.selectable:hover {
opacity: 0.5;
transition: 250ms;
overflow: hidden;
padding: 0 !important;
order: 100;
}
.hotswapAvatar:hover {
opacity: 1; opacity: 1;
background-color: transparent !important; background-color: transparent !important;
cursor: pointer; cursor: pointer;
} }
.hotswapAvatar .avatar_collage, .avatar.avatar_collage img {
.hotswapAvatar.group_select {
border-radius: var(--avatar-base-border-radius-round) !important;
position: relative;
overflow: hidden;
min-width: var(--avatar-base-width) !important;
}
.hotswapAvatar.group_select .avatar.avatar_collage img {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
@ -921,16 +904,6 @@ hr {
border: 1px solid var(--SmartThemeBorderColor); border: 1px solid var(--SmartThemeBorderColor);
} }
.hotswapAvatar .avatar {
width: var(--avatar-base-width) !important;
height: var(--avatar-base-height) !important;
object-fit: cover;
object-position: center center;
border-radius: var(--avatar-base-border-radius-round) !important;
box-shadow: 0 0 5px var(--black50a);
}
.hotswapAvatar img,
.avatar img { .avatar img {
width: var(--avatar-base-width); width: var(--avatar-base-width);
height: var(--avatar-base-height); height: var(--avatar-base-height);
@ -943,10 +916,17 @@ hr {
} }
.bogus_folder_select .avatar, .bogus_folder_select .avatar,
.character_select .avatar { .character_select .avatar,
.avatars_inline .avatar {
flex: unset; flex: unset;
} }
.avatars_inline {
flex-wrap: wrap;
overflow: hidden;
max-height: calc(var(--avatar-base-height) + 2 * var(--avatar-base-border-radius));
}
.bogus_folder_select .avatar { .bogus_folder_select .avatar {
justify-content: center; justify-content: center;
background-color: var(--SmartThemeBlurTintColor); background-color: var(--SmartThemeBlurTintColor);
@ -956,24 +936,24 @@ hr {
outline-color: var(--SmartThemeBorderColor); outline-color: var(--SmartThemeBorderColor);
} }
.avatars_inline .avatar, .avatars_inline_small .avatar,
.avatars_inline .avatar img { .avatars_inline_small .avatar img {
width: calc(var(--avatar-base-width) * var(--inline-avatar-factor)); width: calc(var(--avatar-base-width) * var(--inline-avatar-small-factor));
height: calc(var(--avatar-base-height) * var(--inline-avatar-factor)); height: calc(var(--avatar-base-height) * var(--inline-avatar-small-factor));
} }
.bogus_folder_avatars_block { .avatars_inline_small {
flex-wrap: wrap; height: calc(var(--avatar-base-height) * var(--inline-avatar-small-factor) + 2 * var(--avatar-base-border-radius));
overflow: hidden;
height: calc(var(--avatar-base-height) * var(--inline-avatar-factor) + 2 * var(--avatar-base-border-radius));
} }
.bogus_folder_select:not(.folder_closed) .bogus_folder_avatars_block { .bogus_folder_select:not(.folder_closed) .bogus_folder_avatars_block {
opacity: 1 !important; opacity: 1 !important;
} }
.bogus_folder_avatars_block .avatar { .avatars_inline .avatar {
margin: calc(var(--avatar-base-border-radius)) 0; margin-top: calc(var(--avatar-base-border-radius));
margin-left: calc(var(--avatar-base-border-radius));
margin-bottom: calc(var(--avatar-base-border-radius));
} }
.group_select_block_list { .group_select_block_list {
@ -1627,6 +1607,11 @@ input[type=search]:focus::-webkit-search-cancel-button {
margin-bottom: 1px; margin-bottom: 1px;
} }
.character_select.inline_avatar {
padding: unset;
border-radius: var(--avatar-base-border-radius-round);
}
/*applies to char list and mes_text char display name*/ /*applies to char list and mes_text char display name*/
.ch_name { .ch_name {