diff --git a/public/script.js b/public/script.js
index c38d423aa..59aba505a 100644
--- a/public/script.js
+++ b/public/script.js
@@ -989,16 +989,28 @@ export async function selectCharacterById(id) {
}
}
-function getTagBlock(item) {
- const count = Object.values(tag_map).flat().filter(x => x == item.id).length;
+function getTagBlock(item, entities) {
+ let count = 0;
+
+ for (const entity of entities) {
+ if (entitiesFilter.isElementTagged(entity, item.id)) {
+ count++;
+ }
+ }
+
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('.avatar').css({ 'background-color': item.color, 'color': item.color2 });
template.find('.ch_name').text(item.name);
template.find('.bogus_folder_counter').text(count);
return template;
}
+function getBackBlock() {
+ const template = $('#bogus_folder_back_template .bogus_folder_select').clone();
+ 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'];
@@ -1060,10 +1072,9 @@ 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) {
+ if (isBogusFolderOpen()) {
entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [] });
}
@@ -1072,8 +1083,11 @@ async function printCharacters(fullRefresh = false) {
}
const storageKey = 'Characters_PerPage';
+ const listId = '#rm_print_characters_block';
+ const entities = getEntitiesList({ doFilter: true });
+
$("#rm_print_characters_pagination").pagination({
- dataSource: getEntitiesList({ doFilter: true }),
+ dataSource: entities,
pageSize: Number(localStorage.getItem(storageKey)) || per_page_default,
sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000],
pageRange: 1,
@@ -1086,20 +1100,25 @@ async function printCharacters(fullRefresh = false) {
formatNavigator: PAGINATION_TEMPLATE,
showNavigator: true,
callback: function (data) {
- $("#rm_print_characters_block").empty();
- for (const i of data) {
- if (i.type === 'character') {
- $("#rm_print_characters_block").append(getCharacterBlock(i.item, i.id));
- }
- if (i.type === 'group') {
- $("#rm_print_characters_block").append(getGroupBlock(i.item));
- }
- if (i.type === 'tag') {
- $("#rm_print_characters_block").append(getTagBlock(i.item));
- }
+ $(listId).empty();
+ if (isBogusFolderOpen()) {
+ $(listId).append(getBackBlock());
}
if (!data.length) {
- $("#rm_print_characters_block").append(getEmptyBlock());
+ $(listId).append(getEmptyBlock());
+ }
+ for (const i of data) {
+ switch (i.type) {
+ case 'character':
+ $(listId).append(getCharacterBlock(i.item, i.id));
+ break;
+ case 'group':
+ $(listId).append(getGroupBlock(i.item));
+ break;
+ case 'tag':
+ $(listId).append(getTagBlock(i.item, entities));
+ break;
+ }
}
eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
},
@@ -1110,26 +1129,60 @@ async function printCharacters(fullRefresh = false) {
saveCharactersPage = e;
},
afterRender: function () {
- $('#rm_print_characters_block').scrollTop(0);
+ $(listId).scrollTop(0);
},
});
favsToHotswap();
}
-export function getEntitiesList({ doFilter } = {}) {
- let entities = [];
- entities.push(...characters.map((item, index) => ({ item, id: index, type: 'character' })));
- entities.push(...groups.map((item) => ({ item, id: item.id, type: 'group' })));
+/**
+ * Indicates whether a user is currently in a bogus folder.
+ * @returns {boolean} If currently viewing a folder
+ */
+function isBogusFolderOpen() {
+ return !!entitiesFilter.getFilterData(FILTER_TYPES.TAG)?.bogus;
+}
- if (power_user.bogus_folders) {
- entities.push(...tags.map((item) => ({ item, id: item.id, type: 'tag' })));
+export function getEntitiesList({ doFilter } = {}) {
+ function characterToEntity(character, id) {
+ return { item: character, id, type: 'character' };
}
+ function groupToEntity(group) {
+ return { item: group, id: group.id, type: 'group' };
+ }
+
+ function tagToEntity(tag) {
+ return { item: structuredClone(tag), id: tag.id, type: 'tag' };
+ }
+
+ let entities = [
+ ...characters.map((item, index) => characterToEntity(item, index)),
+ ...groups.map(item => groupToEntity(item)),
+ ...(power_user.bogus_folders ? tags.map(item => tagToEntity(item)) : []),
+ ];
+
if (doFilter) {
entities = entitiesFilter.applyFilters(entities);
}
+ if (isBogusFolderOpen()) {
+ // Get tags of entities within the bogus folder
+ const filterData = structuredClone(entitiesFilter.getFilterData(FILTER_TYPES.TAG));
+ entities = entities.filter(x => x.type !== 'tag');
+ const otherTags = tags.filter(x => !filterData.selected.includes(x.id));
+ const bogusTags = [];
+ for (const entity of entities) {
+ for (const tag of otherTags) {
+ if (!bogusTags.includes(tag) && entitiesFilter.isElementTagged(entity, tag.id)) {
+ bogusTags.push(tag);
+ }
+ }
+ }
+ entities.push(...bogusTags.map(item => tagToEntity(item)));
+ }
+
sortEntitiesList(entities);
return entities;
}
@@ -7523,8 +7576,25 @@ jQuery(async function () {
$(document).on("click", ".bogus_folder_select", function () {
const tagId = $(this).attr('tagid');
console.log('Bogus folder clicked', tagId);
- entitiesFilter.setFilterData(FILTER_TYPES.TAG, { excluded: [], selected: [tagId], bogus: true, });
- })
+
+ const filterData = structuredClone(entitiesFilter.getFilterData(FILTER_TYPES.TAG));
+
+ if (!Array.isArray(filterData.selected)) {
+ filterData.selected = [];
+ filterData.excluded = [];
+ filterData.bogus = false;
+ }
+
+ if (tagId === 'back') {
+ filterData.selected.pop();
+ filterData.bogus = filterData.selected.length > 0;
+ } else {
+ filterData.selected.push(tagId);
+ filterData.bogus = true;
+ }
+
+ entitiesFilter.setFilterData(FILTER_TYPES.TAG, filterData);
+ });
$(document).on("input", ".edit_textarea", function () {
scroll_holder = $("#chat").scrollTop();
diff --git a/public/scripts/filters.js b/public/scripts/filters.js
index f382845cc..ca2e5a103 100644
--- a/public/scripts/filters.js
+++ b/public/scripts/filters.js
@@ -69,6 +69,20 @@ export class FilterHelper {
return data.filter(entity => fuzzySearchResults.includes(entity.uid));
}
+ /**
+ * Checks if the given entity is tagged with the given tag ID.
+ * @param {object} entity Searchable entity
+ * @param {string} tagId Tag ID to check
+ * @returns {boolean} Whether the entity is tagged with the given tag ID
+ */
+ isElementTagged(entity, tagId) {
+ const isCharacter = entity.type === 'character';
+ const lookupValue = isCharacter ? entity.item.avatar : String(entity.id);
+ const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
+
+ return isTagged;
+ }
+
/**
* Applies a tag filter to the data.
* @param {any[]} data The data to filter.
@@ -82,19 +96,12 @@ export class FilterHelper {
return data;
}
- function isElementTagged(entity, tagId) {
- const isCharacter = entity.type === 'character';
- const lookupValue = isCharacter ? entity.item.avatar : String(entity.id);
- const isTagged = Array.isArray(tag_map[lookupValue]) && tag_map[lookupValue].includes(tagId);
- return isTagged;
- }
-
- function getIsTagged(entity) {
- const tagFlags = selected.map(tagId => isElementTagged(entity, tagId));
+ const getIsTagged = (entity) => {
+ const tagFlags = selected.map(tagId => this.isElementTagged(entity, tagId));
const trueFlags = tagFlags.filter(x => x);
const isTagged = TAG_LOGIC_AND ? tagFlags.length === trueFlags.length : trueFlags.length > 0;
- const excludedTagFlags = excluded.map(tagId => isElementTagged(entity, tagId));
+ const excludedTagFlags = excluded.map(tagId => this.isElementTagged(entity, tagId));
const isExcluded = excludedTagFlags.includes(true);
if (isExcluded) {
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index efc4c9e14..51f1e1c09 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -1649,7 +1649,17 @@ function sortEntitiesList(entities) {
return;
}
- entities.sort((a, b) => sortFunc(a.item, b.item));
+ entities.sort((a, b) => {
+ if (a.type === 'tag' && b.type !== 'tag') {
+ return -1;
+ }
+
+ if (a.type !== 'tag' && b.type === 'tag') {
+ return 1;
+ }
+
+ return sortFunc(a.item, b.item);
+ });
}
async function saveTheme() {
diff --git a/public/scripts/tags.js b/public/scripts/tags.js
index 02425f644..a746bb7b7 100644
--- a/public/scripts/tags.js
+++ b/public/scripts/tags.js
@@ -38,14 +38,13 @@ export const tag_filter_types = {
};
const ACTIONABLE_TAGS = {
-
FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star', class: 'filterByFavorites' },
GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users', class: 'filterByGroups' },
+ VIEW: { id: 2, name: 'Manage tags', color: 'rgba(150, 100, 100, 0.5)', action: onViewTagsListClick, icon: 'fa-solid fa-gear', class: 'manageTags' },
HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags', class: 'showTagList' },
}
const InListActionable = {
- VIEW: { id: 2, name: 'Manage tags', color: 'rgba(150, 100, 100, 0.5)', action: onViewTagsListClick, icon: 'fa-solid fa-gear' },
}
const DEFAULT_TAGS = [
@@ -321,9 +320,9 @@ function appendTagToList(listElement, tag, { removable, selectable, action, isGe
tagElement.on('click', () => action.bind(tagElement)(filter));
tagElement.addClass('actionable');
}
- if (action && tag.id === 2) {
+ /*if (action && tag.id === 2) {
tagElement.addClass('innerActionable hidden');
- }
+ }*/
$(listElement).append(tagElement);
}
From a02504381a5c1e133dd793d040a4ff69819d6439 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 11 Nov 2023 16:12:02 +0200
Subject: [PATCH 03/16] Forbid trigger command while the group is generating
---
public/scripts/slash-commands.js | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index bbc782c70..b1af99469 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -26,7 +26,7 @@ import {
setCharacterName,
} from "../script.js";
import { getMessageTimeStamp } from "./RossAscends-mods.js";
-import { groups, resetSelectedGroup, selected_group } from "./group-chats.js";
+import { groups, is_group_generating, resetSelectedGroup, selected_group } from "./group-chats.js";
import { getRegexedString, regex_placement } from "./extensions/regex/engine.js";
import { chat_styles, power_user } from "./power-user.js";
import { autoSelectPersona } from "./personas.js";
@@ -270,7 +270,12 @@ async function unhideMessageCallback(_, arg) {
async function triggerGroupMessageCallback(_, arg) {
if (!selected_group) {
- toastr.warning("Cannot run this command outside of a group chat.");
+ toastr.warning("Cannot run trigger command outside of a group chat.");
+ return;
+ }
+
+ if (is_group_generating) {
+ toastr.warning("Cannot run trigger command while the group reply is generating.");
return;
}
From 28bb5db04f369359be5836a4fd8d065ececcc4a2 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 11 Nov 2023 16:21:20 +0200
Subject: [PATCH 04/16] Add new settings to default/settings.json.
---
default/settings.json | 2 ++
1 file changed, 2 insertions(+)
diff --git a/default/settings.json b/default/settings.json
index fd4edfda7..f8c4b54bf 100644
--- a/default/settings.json
+++ b/default/settings.json
@@ -163,6 +163,8 @@
"custom_stopping_strings_macro": true,
"fuzzy_search": true,
"encode_tags": false,
+ "enableLabMode": false,
+ "enableZenSliders": false,
"ui_mode": 1
},
"extension_settings": {
From 8a8880fca12f0e4edf656defdd8e759b6121cf49 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 11 Nov 2023 16:31:13 +0200
Subject: [PATCH 05/16] Visual touch-up
---
public/css/tags.css | 2 +-
public/scripts/RossAscends-mods.js | 2 +-
public/scripts/templates/macros.html | 46 ++++++++++++++--------------
3 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/public/css/tags.css b/public/css/tags.css
index 788e7dc05..3ad18c468 100644
--- a/public/css/tags.css
+++ b/public/css/tags.css
@@ -13,7 +13,7 @@
.tag_view_item {
display: flex;
flex-direction: row;
- align-items: baseline;
+ align-items: center;
gap: 10px;
margin-bottom: 5px;
}
diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js
index b40e5f85a..35f7b1c2c 100644
--- a/public/scripts/RossAscends-mods.js
+++ b/public/scripts/RossAscends-mods.js
@@ -371,7 +371,7 @@ function RA_checkOnlineStatus() {
connection_made = false;
} else {
if (online_status !== undefined && online_status !== "no_connection") {
- $("#send_textarea").attr("placeholder", `Type a message, or /? for command list`); //on connect, placeholder tells user to type message
+ $("#send_textarea").attr("placeholder", `Type a message, or /? for help`); //on connect, placeholder tells user to type message
$('#send_form').removeClass("no-connection");
$("#API-status-top").removeClass("fa-plug-circle-exclamation redOverlayGlow");
$("#API-status-top").addClass("fa-plug");
diff --git a/public/scripts/templates/macros.html b/public/scripts/templates/macros.html
index bd58ae1fe..dc2844439 100644
--- a/public/scripts/templates/macros.html
+++ b/public/scripts/templates/macros.html
@@ -1,26 +1,26 @@
System-wide Replacement Macros (in order of evaluation):
- - {{original}} - global prompts defined in API settings. Only valid in Advanced Definitions prompt overrides.
- - {{input}} - the user input
- - {{description}} - the Character's Description
- - {{personality}} - the Character's Personality
- - {{scenario}} - the Character's Scenario
- - {{persona}} - your current Persona Description
- - {{mesExamples}} - the Character's Dialogue Examples
- - {{user}} - your current Persona username
- - {{char}} - the Character's name
- - {{lastMessageId}} - index # of the latest chat message. Useful for slash command batching.
- - {{// (note)}} - you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.
- - {{time}} - the current time
- - {{date}} - the current date
- - {{weekday}} - the current weekday
- - {{isotime}} - the current ISO date (YYYY-MM-DD)
- - {{isodate}} - the current ISO time (24-hour clock)
- - {{datetimeformat …}} - the current date/time in the specified format, e. g. for German date/time: {{datetimeformat DD.MM.YYYY HH:mm}}
- - {{time_UTC±#}} - the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2
- - {{idle_duration}} - the time since the last user message was sent
- - {{bias "text here"}} - sets a behavioral bias for the AI until the next user input. Quotes around the text are important.
- - {{random:(args)}} - returns a random item from the list. (ex: {{random:1,2,3,4}} will return 1 of the 4 numbers at random. Works with text lists too.
- - {{roll:(formula)}} - rolls a dice. (ex: {{roll:1d6}} will roll a 6-sided dice and return a number between 1 and 6)
- - {{banned "text here"}} - dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.
+ - {{original}} – global prompts defined in API settings. Only valid in Advanced Definitions prompt overrides.
+ - {{input}} – the user input
+ - {{description}} – the Character's Description
+ - {{personality}} – the Character's Personality
+ - {{scenario}} – the Character's Scenario
+ - {{persona}} – your current Persona Description
+ - {{mesExamples}} – the Character's Dialogue Examples
+ - {{user}} – your current Persona username
+ - {{char}} – the Character's name
+ - {{lastMessageId}} – index # of the latest chat message. Useful for slash command batching.
+ - {{// (note)}} – you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.
+ - {{time}} – the current time
+ - {{date}} – the current date
+ - {{weekday}} – the current weekday
+ - {{isotime}} – the current ISO date (YYYY-MM-DD)
+ - {{isodate}} – the current ISO time (24-hour clock)
+ - {{datetimeformat …}} – the current date/time in the specified format, e. g. for German date/time: {{datetimeformat DD.MM.YYYY HH:mm}}
+ - {{time_UTC±#}} – the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2
+ - {{idle_duration}} – the time since the last user message was sent
+ - {{bias "text here"}} – sets a behavioral bias for the AI until the next user input. Quotes around the text are important.
+ - {{random:(args)}} – returns a random item from the list. (ex: {{random:1,2,3,4}} will return 1 of the 4 numbers at random. Works with text lists too.
+ - {{roll:(formula)}} – rolls a dice. (ex: {{roll:1d6}} will roll a 6- sided dice and return a number between 1 and 6)
+ - {{banned "text here"}} – dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.
From f1d0e39d39e868a4129de1e34aa3605ed658bb91 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 11 Nov 2023 16:39:54 +0200
Subject: [PATCH 06/16] Require a name for dummy personas
---
public/index.html | 2 +-
public/scripts/personas.js | 18 +++++++++++++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/public/index.html b/public/index.html
index 575d7d2e5..205ae4691 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3088,7 +3088,7 @@
diff --git a/public/scripts/personas.js b/public/scripts/personas.js
index fc91e8008..8a3f0341d 100644
--- a/public/scripts/personas.js
+++ b/public/scripts/personas.js
@@ -39,7 +39,23 @@ async function uploadUserAvatar(url, name) {
}
async function createDummyPersona() {
- await uploadUserAvatar(default_avatar);
+ const personaName = await callPopup('
Enter a name for this persona:
', 'input', '');
+
+ if (!personaName) {
+ console.debug('User cancelled creating dummy persona');
+ return;
+ }
+
+ // Date + name (only ASCII) to make it unique
+ const avatarId = `${Date.now()}-${personaName.replace(/[^a-zA-Z0-9]/g, '')}.png`;
+ power_user.personas[avatarId] = personaName;
+ power_user.persona_descriptions[avatarId] = {
+ description: '',
+ position: persona_description_positions.IN_PROMPT,
+ };
+
+ await uploadUserAvatar(default_avatar, avatarId);
+ saveSettingsDebounced();
}
export async function convertCharacterToPersona(characterId = null) {
From 91a1cc81a0b5708d7ab76cc41a624091127cb42a Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 11 Nov 2023 18:25:43 +0200
Subject: [PATCH 07/16] #1242 Add aux field selector
---
public/index.html | 9 ++++++++-
public/script.js | 7 ++++---
public/scripts/power-user.js | 9 +++++++++
3 files changed, 21 insertions(+), 4 deletions(-)
diff --git a/public/index.html b/public/index.html
index 9c0f396cd..909af8577 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2680,7 +2680,7 @@