Add create deck functionality to context menu

This commit is contained in:
artisticMink 2023-10-30 19:24:45 +01:00
parent efdd280646
commit 015c83aec1
3 changed files with 104 additions and 12 deletions

View File

@ -102,7 +102,7 @@
<div id="character_context_menu" class="hidden">
<ul>
<li><button id="character_context_menu_favorite">Favorite</button></li>
<li><button id="character_context_menu_collection">Collection</button></li>
<li><button id="character_context_menu_deck">Deck</button></li>
<li><button id="character_context_menu_persona">To Persona</button></li>
<li><button id="character_context_menu_duplicate">Duplicate</button></li>
<li><button id="character_context_menu_delete">Delete</button></li>

View File

@ -954,6 +954,9 @@ async function printCharacters(fullRefresh = false) {
callback: function (data) {
$("#rm_print_characters_block").empty();
for (const i of data) {
if (i.type === 'deck') {
$("#rm_print_characters_block").append(getCharacterBlock(i.item));
}
if (i.type === 'character') {
$("#rm_print_characters_block").append(getCharacterBlock(i.item, i.id));
}
@ -979,6 +982,7 @@ async function printCharacters(fullRefresh = false) {
export function getEntitiesList({ doFilter } = {}) {
let entities = [];
entities.push(...(settings.characterDecks?.decks ?? []).map((item) => ({ item, id: item.id, type: 'deck' })));
entities.push(...characters.map((item, index) => ({ item, id: index, type: 'character' })));
entities.push(...groups.map((item) => ({ item, id: item.id, type: 'group' })));
@ -5170,6 +5174,7 @@ async function saveSettings(type) {
horde_settings: horde_settings,
power_user: power_user,
extension_settings: extension_settings,
characterDecks: settings.characterDecks,
tags: tags,
tag_map: tag_map,
nai_settings: nai_settings,

View File

@ -2,14 +2,18 @@
import {
callPopup,
characters, deleteCharacter,
characters,
deleteCharacter,
event_types,
eventSource,
getCharacters,
getRequestHeaders, handleDeleteCharacter, this_chid
getRequestHeaders,
saveSettings,
settings
} from "../script.js";
import {favsToHotswap} from "./RossAscends-mods.js";
import {convertCharacterToPersona} from "./personas.js";
import {uuidv4} from "./utils.js";
const popupMessage = {
deleteChat(characterCount) {
@ -20,9 +24,10 @@ const popupMessage = {
<span>Also delete the chat files</span>
</label><br></b>`;
},
exportCharacters(characterCount) {
return `<h3>Export ${characterCount} characters?</h3>`;
}
newDeck() {
return `<br/><p>Create a new character deck with the selected characters.</p>
<h3>Set a name:</h3><br/>`;
},
}
const toggleFavoriteHighlight = (characterId) => {
@ -30,6 +35,64 @@ const toggleFavoriteHighlight = (characterId) => {
element.classList.toggle('is_fav');
}
/**
* Defines a visual grouping of characters
*/
class CharacterDeck {
id = '';
name = '';
/** @type {int[]} */
characters = [];
constructor(id, name, characters = []) {
this.id = id;
this.name = name;
this.characters = characters;
return this;
}
addCharacter = (characterId) => {
this.characters.push(characterId);
return this;
}
static fromObject(obj) {
return new CharacterDeck(obj.id, obj.name, obj.characters);
}
}
/**
* Represents the characterCollections setting
*/
class CharacterDeckCollection {
/** @type {CharacterDeck[]} */
decks = [];
constructor(decks = []) {
if (false === Array.isArray(decks) || decks.some(deck => !(deck instanceof CharacterDeck))) {
throw new Error('All groups must be instances of CharacterGroup');
}
this.decks = decks;
}
addDeck = (deck) => {
if (!(deck instanceof CharacterDeck)) {
throw new Error('Group must be an instance of CharacterGroup');
}
this.decks.push(deck);
return this;
}
static fromObject(object) {
const decks = object.decks.map(deck => CharacterDeck.fromObject(deck));
return new CharacterDeckCollection(decks);
}
}
/**
* Implement a SingletonPattern, allowing access to the group overlay instance
* from everywhere via (new CharacterGroupOverlay())
@ -44,6 +107,7 @@ class CharacterGroupOverlayState {
}
class CharacterContextMenu {
/**
* Duplicate a character
*
@ -91,6 +155,20 @@ class CharacterContextMenu {
static persona = async (characterId) => convertCharacterToPersona(characterId);
static deck = (name = 'New Deck', characterIds = []) => {
let characterDeckCollection = null;
if (settings.characterDecks && Array.isArray(settings.characterDecks.decks)) {
characterDeckCollection = CharacterDeckCollection.fromObject(settings.characterDecks);
} else {
characterDeckCollection = new CharacterDeckCollection();
}
const id = uuidv4();
characterDeckCollection.addDeck(new CharacterDeck(id, name, characterIds));
settings.characterDecks = JSON.parse(JSON.stringify(characterDeckCollection))
}
static delete = async (characterId, deleteChats = false) => {
const character = CharacterContextMenu.getCharacter(characterId);
@ -120,7 +198,7 @@ class CharacterContextMenu {
}
})
}
eventSource.emit('characterDeleted', { id: this_chid, character: characters[this_chid] });
eventSource.emit('characterDeleted', { id: characterId, character: character });
});
}
@ -141,7 +219,8 @@ class CharacterContextMenu {
{id: 'character_context_menu_favorite', callback: characterGroupOverlay.handleContextMenuFavorite},
{id: 'character_context_menu_duplicate', callback: characterGroupOverlay.handleContextMenuDuplicate},
{id: 'character_context_menu_delete', callback: characterGroupOverlay.handleContextMenuDelete},
{id: 'character_context_menu_persona', callback: characterGroupOverlay.handleContextMenuPersona}
{id: 'character_context_menu_persona', callback: characterGroupOverlay.handleContextMenuPersona},
{id: 'character_context_menu_deck', callback: characterGroupOverlay.handleContextMenuCreateDeck}
];
contextMenuItems.forEach(contextMenuItem => document.getElementById(contextMenuItem.id).addEventListener('click', contextMenuItem.callback))
@ -213,11 +292,11 @@ class CharacterGroupOverlay {
* Set up a Sortable grid for the loaded page
*/
onPageLoad = () => {
const elements = [...document.getElementsByClassName(CharacterGroupOverlay.characterClass)];
this.browseState();
const elements = [...document.getElementsByClassName(CharacterGroupOverlay.characterClass)];
elements.forEach(element => element.addEventListener('touchstart', this.handleHold));
elements.forEach(element => element.addEventListener('mousedown', this.handleHold));
elements.forEach(element => element.addEventListener('touchend', this.handleLongPressEnd));
elements.forEach(element => element.addEventListener('mouseup', this.handleLongPressEnd));
elements.forEach(element => element.addEventListener('dragend', this.handleLongPressEnd));
@ -326,9 +405,8 @@ class CharacterGroupOverlay {
.then(() => getCharacters())
.then(() => this.browseState())
handleContextMenuPersona = () => { Promise.all(this.selectedCharacters.map(async characterId => CharacterContextMenu.persona(characterId)))
handleContextMenuPersona = () => Promise.all(this.selectedCharacters.map(async characterId => CharacterContextMenu.persona(characterId)))
.then(() => this.browseState());
}
handleContextMenuDelete = () => {
callPopup(
@ -340,6 +418,15 @@ class CharacterGroupOverlay {
);
}
handleContextMenuCreateDeck = () => {
callPopup(popupMessage.newDeck, 'input').then(
(resolve) => {
CharacterContextMenu.deck(resolve, this.selectedCharacters);
saveSettings().then(async () => await getCharacters());
}
);
}
addStateChangeCallback = callback => this.stateChangeCallbacks.push(callback);
selectCharacter = characterId => this.selectedCharacters.push(String(characterId));