mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
User persona management block. Persona descriptions. Dummy personas. Change persona avatar
This commit is contained in:
@ -2001,7 +2001,7 @@
|
||||
|
||||
<div id="user-settings-button" class="drawer">
|
||||
<div class="drawer-toggle">
|
||||
<div class="drawer-icon fa-solid fa-face-smile closedIcon" title="User Settings"></div>
|
||||
<div class="drawer-icon fa-solid fa-user-cog closedIcon" title="User Settings"></div>
|
||||
</div>
|
||||
<div id="user-settings-block" class="drawer-content closedDrawer">
|
||||
<div class="flex-container wide100p alignitemscenter spaceBetween">
|
||||
@ -2009,7 +2009,7 @@
|
||||
<div id="version_display"></div>
|
||||
</div>
|
||||
<div class="flex-container spaceEvenly">
|
||||
<div name="UI Customization" class="flex-container drawer25pWidth">
|
||||
<div name="UI Customization" class="flex-container drawer33pWidth">
|
||||
<div class="ui-settings">
|
||||
<h4><span data-i18n="UI Customization">UI Customization</span></h4>
|
||||
<div>
|
||||
@ -2098,13 +2098,25 @@
|
||||
<span data-i18n="Movable UI Panels">Movable UI Panels</span>
|
||||
</label>
|
||||
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<h4 data-i18n="Send on Enter">
|
||||
Send on Enter
|
||||
</h4>
|
||||
<select id="send_on_enter">
|
||||
<option value="-1"><span data-i18n="Always disabled">Always disabled</span></option>
|
||||
<option value="0"><span data-i18n="Automatic (desktop)">Automatic (desktop)</span>
|
||||
</option>
|
||||
<option value="1"><span data-i18n="Always enabled">Always enabled</span></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="movingUIreset" class="menu_button whitespacenowrap" data-i18n="Reset Panels">
|
||||
Reset Panels</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="UI-Theme-Block" class="flex-container flexFlowColumn drawer25pWidth">
|
||||
<div id="UI-Theme-Block" class="flex-container flexFlowColumn drawer33pWidth">
|
||||
<div id="color-picker-block" class="flex-container flexFlowColumn">
|
||||
<h4><span data-i18n="UI Colors">UI Colors</span></h4>
|
||||
<div class="flex-container">
|
||||
@ -2201,7 +2213,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="power-user-options-block" class="flex-container drawer25pWidth">
|
||||
<div id="power-user-options-block" class="flex-container drawer33pWidth">
|
||||
<div id="power-user-option-checkboxes">
|
||||
<h4 data-i18n="Power User Options">Power User Options</h4>
|
||||
<label for="swipes-checkbox">
|
||||
@ -2253,74 +2265,33 @@
|
||||
<span data-i18n="Never Resize Avatars">Never Resize Avatars</span>
|
||||
</label>
|
||||
|
||||
<div class="inline-drawer wide100p flexFlowColumn">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Auto-swipe</b>
|
||||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label class="checkbox_label" for="auto_swipe">
|
||||
<input id="auto_swipe" type="checkbox" />
|
||||
Enabled
|
||||
</label>
|
||||
<div>Minimum generated message length</div>
|
||||
<input id="auto_swipe_minimum_length" name="auto_swipe_minimum_length" type="number" min="0" step="1" value="0" class="text_pole">
|
||||
<div>Blacklisted words</div>
|
||||
<div class="auto_swipe">
|
||||
<textarea id="auto_swipe_blacklist" name="auto_swipe_blacklist" placeholder="words you dont want generated separated by comma ','" class="text_pole textarea_compact" maxlength="5000" value="" autocomplete="off" rows="3"></textarea>
|
||||
<div>Blacklisted word count to swipe</div>
|
||||
<input id="auto_swipe_blacklist_threshold" name="auto_swipe_blacklist_threshold" type="number" min="0" step="1" value="1" class="text_pole">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="reload_chat" class="menu_button whitespacenowrap" data-i18n="Reload Chat">
|
||||
Reload Chat
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div name="NameAndAvatar" class="flex-container flexFlowColumn drawer25pWidth">
|
||||
|
||||
|
||||
<div class="inline-drawer wide100p flexFlowColumn">
|
||||
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<h4 data-i18n="Send on Enter">
|
||||
Send on Enter
|
||||
</h4>
|
||||
<select id="send_on_enter">
|
||||
<option value="-1"><span data-i18n="Always disabled">Always disabled</span></option>
|
||||
<option value="0"><span data-i18n="Automatic (desktop)">Automatic (desktop)</span>
|
||||
</option>
|
||||
<option value="1"><span data-i18n="Always enabled">Always enabled</span></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Auto-swipe</b>
|
||||
<div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
|
||||
<label class="checkbox_label" for="auto_swipe">
|
||||
<input id="auto_swipe" type="checkbox" />
|
||||
Enabled
|
||||
</label>
|
||||
<div>Minimum generated message length</div>
|
||||
<input id="auto_swipe_minimum_length" name="auto_swipe_minimum_length" type="number" min="0" step="1" value="0" class="text_pole">
|
||||
<div>Blacklisted words</div>
|
||||
<div class="auto_swipe">
|
||||
<textarea id="auto_swipe_blacklist" name="auto_swipe_blacklist" placeholder="words you dont want generated separated by comma ','" class="text_pole textarea_compact" maxlength="5000" value="" autocomplete="off" rows="3"></textarea>
|
||||
<div>Blacklisted word count to swipe</div>
|
||||
<input id="auto_swipe_blacklist_threshold" name="auto_swipe_blacklist_threshold" type="number" min="0" step="1" value="1" class="text_pole">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div name="NameChanger">
|
||||
<h4 data-i18n="Name">Name</h4>
|
||||
<div class="change_name">
|
||||
<input id="your_name" name="your_name" placeholder="Enter your name" class="text_pole wide100p" maxlength="50" value="" autocomplete="off">
|
||||
<div id="your_name_button" class="menu_button fa-solid fa-check" title="Click to set a new User Name">
|
||||
</div>
|
||||
<div id="lock_user_name" class="menu_button fa-solid fa-unlock" title="Click to lock your selected persona to the current chat. Click again to remove the lock.">
|
||||
</div>
|
||||
<div id="sync_name_button" class="menu_button fa-solid fa-sync" title="Click to set user name for all messages">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div name="AvatarSelector">
|
||||
<h4 data-i18n="Your Avatar">Your Persona</h4>
|
||||
<div id="user_avatar_block">
|
||||
<div class="avatar_upload">+</div>
|
||||
</div>
|
||||
|
||||
<form id="form_upload_avatar" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<input type="file" id="avatar_upload_file" accept="image/*" name="avatar">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -2383,6 +2354,60 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="persona-management-button" class="drawer">
|
||||
<div class="drawer-toggle">
|
||||
<div class="drawer-icon fa-solid fa-face-smile closedIcon" title="Persona Management"></div>
|
||||
</div>
|
||||
<div class="drawer-content closedDrawer">
|
||||
<div class="flex-container wide100p alignitemscenter spaceBetween">
|
||||
<h3><span data-i18n="Persona Management">Persona Management</span></h3>
|
||||
|
||||
<div id="persona-management-block" class="flex-container wide100p">
|
||||
<div class="flex1">
|
||||
<h4 data-i18n="Name">Name</h4>
|
||||
<div class="change_name">
|
||||
<input id="your_name" name="your_name" placeholder="Enter your name" class="text_pole wide100p" maxlength="50" value="" autocomplete="off">
|
||||
<div id="your_name_button" class="menu_button fa-solid fa-check" title="Click to set a new User Name">
|
||||
</div>
|
||||
<div id="lock_user_name" class="menu_button fa-solid fa-unlock" title="Click to lock your selected persona to the current chat. Click again to remove the lock.">
|
||||
</div>
|
||||
<div id="sync_name_button" class="menu_button fa-solid fa-sync" title="Click to set user name for all messages">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 data-i18n="Name">Persona Description</h4>
|
||||
<textarea id="persona_description" name="persona_description" placeholder="Example: [{{user}} is a 28-year-old Romanian cat girl.]" class="text_pole textarea_compact" maxlength="5000" value="" autocomplete="off" rows="4"></textarea>
|
||||
<label for="persona_description_position">Position:</label>
|
||||
<select id="persona_description_position">
|
||||
<option value="0">Before Character Card</option>
|
||||
<option value="1">After Character Card</option>
|
||||
<option value="2">Top of Author's Note</option>
|
||||
<option value="3">Bottom of Author's Note</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex1">
|
||||
<h4 data-i18n="Your Avatar" class="title_restorable">
|
||||
<span>Your Persona</span>
|
||||
<div id="create_dummy_persona" class="menu_button menu_button_icon" title="Create a dummy persona">
|
||||
<i class="fa-solid fa-person-circle-question fa-fw"></i>
|
||||
<span>Blank</span>
|
||||
</div>
|
||||
</h4>
|
||||
<div id="user_avatar_block">
|
||||
<div class="avatar_upload">+</div>
|
||||
</div>
|
||||
|
||||
<form id="form_upload_avatar" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<input type="file" id="avatar_upload_file" accept="image/*" name="avatar">
|
||||
<input type="hidden" id="avatar_upload_overwrite" name="overwrite_name" value="">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="rightNavHolder" class="drawer">
|
||||
<div id="unimportantYes" class="drawer-toggle drawer-header">
|
||||
<div id="rightNavDrawerIcon" class="drawer-icon fa-solid fa-address-card closedIcon" title="Character Management">
|
||||
@ -3369,8 +3394,8 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="avatar-buttons avatar-buttons-bottom">
|
||||
<button class="menu_button set_user_info" title="Under construction">
|
||||
<i class="fa-solid fa-circle-user"></i>
|
||||
<button class="menu_button set_persona_image" title="Change persona image">
|
||||
<i class="fa-solid fa-image"></i>
|
||||
</button>
|
||||
|
||||
<button class="menu_button delete_avatar" title="Delete persona">
|
||||
|
259
public/script.js
259
public/script.js
@ -69,6 +69,7 @@ import {
|
||||
formatInstructModeChat,
|
||||
formatInstructStoryString,
|
||||
formatInstructModePrompt,
|
||||
persona_description_positions,
|
||||
} from "./scripts/power-user.js";
|
||||
|
||||
import {
|
||||
@ -151,7 +152,7 @@ import {
|
||||
import { EventEmitter } from './scripts/eventemitter.js';
|
||||
import { context_settings, loadContextTemplatesFromSettings } from "./scripts/context-template.js";
|
||||
import { markdownExclusionExt } from "./scripts/showdown-exclusion.js";
|
||||
import { setFloatingPrompt } from "./scripts/extensions/floating-prompt/index.js";
|
||||
import { NOTE_MODULE_NAME, metadata_keys, setFloatingPrompt, shouldWIAddPrompt } from "./scripts/extensions/floating-prompt/index.js";
|
||||
|
||||
//exporting functions and vars for mods
|
||||
export {
|
||||
@ -1554,6 +1555,28 @@ function cleanGroupMessage(getMessage) {
|
||||
return getMessage;
|
||||
}
|
||||
|
||||
function getPersonaDescription(storyString) {
|
||||
if (!power_user.persona_description) {
|
||||
return storyString;
|
||||
}
|
||||
|
||||
switch (power_user.persona_description_position) {
|
||||
case persona_description_positions.BEFORE_CHAR:
|
||||
return `${power_user.persona_description}\n${storyString}`;
|
||||
case persona_description_positions.AFTER_CHAR:
|
||||
return `${storyString}\n${power_user.persona_description}`;
|
||||
default:
|
||||
if (shouldWIAddPrompt) {
|
||||
const originalAN = extension_prompts[NOTE_MODULE_NAME].value
|
||||
const ANWithDesc = persona_description_positions.TOP_AN
|
||||
? `${power_user.persona_description}\n${originalAN}`
|
||||
: `${originalAN}\n${power_user.persona_description}`;
|
||||
setExtensionPrompt(NOTE_MODULE_NAME, ANWithDesc, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
|
||||
}
|
||||
return storyString;
|
||||
}
|
||||
}
|
||||
|
||||
function getAllExtensionPrompts() {
|
||||
const value = Object
|
||||
.values(extension_prompts)
|
||||
@ -1998,12 +2021,6 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
console.log(`Core/all messages: ${coreChat.length}/${chat.length}`);
|
||||
}
|
||||
|
||||
if (main_api === 'openai') {
|
||||
message_already_generated = ''; // OpenAI doesn't have multigen
|
||||
setOpenAIMessages(coreChat);
|
||||
setOpenAIMessageExamples(mesExamplesArray);
|
||||
}
|
||||
|
||||
let storyString = "";
|
||||
|
||||
if (is_pygmalion) {
|
||||
@ -2062,11 +2079,19 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
setFloatingPrompt();
|
||||
// Add WI to prompt (and also inject WI to AN value via hijack)
|
||||
let { worldInfoString, worldInfoBefore, worldInfoAfter } = await getWorldInfoPrompt(chat2, this_max_context);
|
||||
// Add persona description to prompt
|
||||
storyString = getPersonaDescription(storyString);
|
||||
// Call combined AN into Generate
|
||||
let allAnchors = getAllExtensionPrompts();
|
||||
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
|
||||
let zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' ');
|
||||
|
||||
if (main_api === 'openai') {
|
||||
message_already_generated = ''; // OpenAI doesn't have multigen
|
||||
setOpenAIMessages(coreChat);
|
||||
setOpenAIMessageExamples(mesExamplesArray);
|
||||
}
|
||||
|
||||
// Moved here to not overflow the Poe context with added prompt bits
|
||||
if (main_api == 'poe') {
|
||||
allAnchors = appendPoeAnchors(type, allAnchors);
|
||||
@ -3954,8 +3979,6 @@ function changeMainAPI() {
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
async function getUserAvatars() {
|
||||
$("#user_avatar_block").html(""); //RossAscends: necessary to avoid doubling avatars each refresh.
|
||||
$("#user_avatar_block").append('<div class="avatar_upload">+</div>');
|
||||
const response = await fetch("/getuseravatars", {
|
||||
method: "POST",
|
||||
headers: getRequestHeaders(),
|
||||
@ -3967,6 +3990,8 @@ async function getUserAvatars() {
|
||||
const getData = await response.json();
|
||||
//background = getData;
|
||||
//console.log(getData.length);
|
||||
$("#user_avatar_block").html(""); //RossAscends: necessary to avoid doubling avatars each refresh.
|
||||
$("#user_avatar_block").append('<div class="avatar_upload">+</div>');
|
||||
|
||||
for (var i = 0; i < getData.length; i++) {
|
||||
//console.log(1);
|
||||
@ -3977,6 +4002,56 @@ async function getUserAvatars() {
|
||||
}
|
||||
}
|
||||
|
||||
function setPersonaDescription() {
|
||||
$("#persona_description").val(power_user.persona_description);
|
||||
$("#persona_description_position")
|
||||
.val(power_user.persona_description_position)
|
||||
.find(`option[value='${power_user.persona_description_position}']`)
|
||||
.attr("selected", true);
|
||||
}
|
||||
|
||||
function onPersonaDescriptionPositionInput() {
|
||||
power_user.persona_description_position = Number(
|
||||
$("#persona_description_position").find(":selected").val()
|
||||
);
|
||||
|
||||
if (power_user.personas[user_avatar]) {
|
||||
let object = power_user.persona_descriptions[user_avatar];
|
||||
|
||||
if (!object) {
|
||||
object = {
|
||||
description: power_user.persona_description,
|
||||
position: power_user.persona_description_position,
|
||||
};
|
||||
power_user.persona_descriptions[user_avatar] = object;
|
||||
}
|
||||
|
||||
object.position = power_user.persona_description_position;
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onPersonaDescriptionInput() {
|
||||
power_user.persona_description = $("#persona_description").val();
|
||||
|
||||
if (power_user.personas[user_avatar]) {
|
||||
let object = power_user.persona_descriptions[user_avatar];
|
||||
|
||||
if (!object) {
|
||||
object = {
|
||||
description: power_user.persona_description,
|
||||
position: Number($("#persona_description_position").find(":selected").val()),
|
||||
};
|
||||
power_user.persona_descriptions[user_avatar] = object;
|
||||
}
|
||||
|
||||
object.description = power_user.persona_description;
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function highlightSelectedAvatar() {
|
||||
$("#user_avatar_block").find(".avatar").removeClass("selected");
|
||||
$("#user_avatar_block")
|
||||
@ -4053,9 +4128,20 @@ async function bindUserNameToPersona() {
|
||||
// If the user clicked ok and entered a name, bind the name to the persona
|
||||
console.log(`Binding persona ${avatarId} to name ${personaName}`);
|
||||
power_user.personas[avatarId] = personaName;
|
||||
const descriptor = power_user.persona_descriptions[avatarId];
|
||||
const isCurrentPersona = avatarId === user_avatar;
|
||||
|
||||
// Create a description object if it doesn't exist
|
||||
if (!descriptor) {
|
||||
// If the user is currently using this persona, set the description to the current description
|
||||
power_user.persona_descriptions[avatarId] = {
|
||||
description: isCurrentPersona ? power_user.persona_description : '',
|
||||
position: isCurrentPersona ? power_user.persona_description_position : persona_description_positions.BEFORE_CHAR,
|
||||
};
|
||||
}
|
||||
|
||||
// If the user is currently using this persona, update the name
|
||||
if (avatarId === user_avatar) {
|
||||
if (isCurrentPersona) {
|
||||
console.log(`Auto-updating user name to ${personaName}`);
|
||||
setUserName(personaName);
|
||||
}
|
||||
@ -4063,10 +4149,33 @@ async function bindUserNameToPersona() {
|
||||
// If the user clicked ok, but didn't enter a name, delete the persona
|
||||
console.log(`Unbinding persona ${avatarId}`);
|
||||
delete power_user.personas[avatarId];
|
||||
delete power_user.persona_descriptions[avatarId];
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
await getUserAvatars();
|
||||
setPersonaDescription();
|
||||
}
|
||||
|
||||
async function createDummyPersona() {
|
||||
const fetchResult = await fetch(default_avatar);
|
||||
const blob = await fetchResult.blob();
|
||||
const file = new File([blob], "avatar.png", { type: "image/png" });
|
||||
const formData = new FormData();
|
||||
formData.append("avatar", file);
|
||||
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: "/uploaduseravatar",
|
||||
data: formData,
|
||||
beforeSend: () => { },
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (data) {
|
||||
await getUserAvatars();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function updateUserLockIcon() {
|
||||
@ -4093,12 +4202,75 @@ function setUserAvatar() {
|
||||
}
|
||||
|
||||
setUserName(personaName);
|
||||
|
||||
const descriptor = power_user.persona_descriptions[user_avatar];
|
||||
|
||||
if (descriptor) {
|
||||
power_user.persona_description = descriptor.description;
|
||||
power_user.persona_description_position = descriptor.position;
|
||||
} else {
|
||||
power_user.persona_description = '';
|
||||
power_user.persona_description_position = persona_description_positions.BEFORE_CHAR;
|
||||
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.BEFORE_CHAR };
|
||||
}
|
||||
|
||||
setPersonaDescription();
|
||||
}
|
||||
}
|
||||
|
||||
async function setUserInfo() {
|
||||
// TODO Replace with actual implementation
|
||||
callPopup('This functionality is under development.<br>Please check back later.', 'text');
|
||||
async function uploadUserAvatar(e) {
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
$("#form_upload_avatar").trigger("reset");
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData($("#form_upload_avatar").get(0));
|
||||
|
||||
const dataUrl = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = resolve;
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
|
||||
const confirmation = await callPopup(getCropPopup(dataUrl.target.result), 'avatarToCrop');
|
||||
if (!confirmation) {
|
||||
return;
|
||||
}
|
||||
|
||||
let url = "/uploaduseravatar";
|
||||
|
||||
if (crop_data !== undefined) {
|
||||
url += `?crop=${encodeURIComponent(JSON.stringify(crop_data))}`;
|
||||
}
|
||||
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
beforeSend: () => { },
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function () {
|
||||
// If the user uploaded a new avatar, we want to make sure it's not cached
|
||||
const name = formData.get("overwrite_name");
|
||||
if (name) {
|
||||
await fetch(getUserAvatar(name), { cache: "no-cache" });
|
||||
reloadUserAvatar();
|
||||
}
|
||||
|
||||
crop_data = undefined;
|
||||
await getUserAvatars();
|
||||
},
|
||||
error: (jqXHR, exception) => { },
|
||||
});
|
||||
|
||||
// Will allow to select the same file twice in a row
|
||||
$("#form_upload_avatar").trigger("reset");
|
||||
}
|
||||
|
||||
async function setDefaultPersona() {
|
||||
@ -4179,6 +4351,7 @@ async function deleteUserAvatar() {
|
||||
if (request.ok) {
|
||||
console.log(`Deleted avatar ${avatarId}`);
|
||||
delete power_user.personas[avatarId];
|
||||
delete power_user.persona_descriptions[avatarId];
|
||||
|
||||
if (avatarId === power_user.default_persona) {
|
||||
toastr.warning('The default persona was deleted. You will need to set a new default persona.', 'Default persona deleted');
|
||||
@ -4215,6 +4388,7 @@ function lockUserNameToChat() {
|
||||
{ timeOut: 10000, extendedTimeOut: 20000, },
|
||||
);
|
||||
power_user.personas[user_avatar] = name1;
|
||||
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.BEFORE_CHAR };
|
||||
}
|
||||
|
||||
chat_metadata['persona'] = user_avatar;
|
||||
@ -4412,6 +4586,7 @@ async function getSettings(type) {
|
||||
user_avatar = settings.user_avatar;
|
||||
reloadUserAvatar();
|
||||
highlightSelectedAvatar();
|
||||
setPersonaDescription();
|
||||
|
||||
//Load the API server URL from settings
|
||||
api_server = settings.api_server;
|
||||
@ -6264,56 +6439,21 @@ $(document).ready(function () {
|
||||
|
||||
$(document).on("click", "#user_avatar_block .avatar", setUserAvatar);
|
||||
$(document).on("click", "#user_avatar_block .avatar_upload", function () {
|
||||
$("#avatar_upload_file").click();
|
||||
$("#avatar_upload_overwrite").val("");
|
||||
$("#avatar_upload_file").trigger('click');
|
||||
});
|
||||
$("#avatar_upload_file").on("change", async function (e) {
|
||||
const file = e.target.files[0];
|
||||
$(document).on("click", "#user_avatar_block .set_persona_image", function () {
|
||||
const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile');
|
||||
|
||||
if (!file) {
|
||||
if (!avatarId) {
|
||||
console.log('no imgfile');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData($("#form_upload_avatar").get(0));
|
||||
|
||||
const dataUrl = await new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = resolve;
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
$('#dialogue_popup').addClass('large_dialogue_popup wide_dialogue_popup');
|
||||
const confirmation = await callPopup(getCropPopup(dataUrl.target.result), 'avatarToCrop');
|
||||
if (!confirmation) {
|
||||
return;
|
||||
}
|
||||
|
||||
let url = "/uploaduseravatar";
|
||||
|
||||
if (crop_data !== undefined) {
|
||||
url += `?crop=${encodeURIComponent(JSON.stringify(crop_data))}`;
|
||||
}
|
||||
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: url,
|
||||
data: formData,
|
||||
beforeSend: () => { },
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: function (data) {
|
||||
if (data.path) {
|
||||
appendUserAvatar(data.path);
|
||||
}
|
||||
crop_data = undefined;
|
||||
},
|
||||
error: (jqXHR, exception) => { },
|
||||
});
|
||||
|
||||
// Will allow to select the same file twice in a row
|
||||
$("#form_upload_avatar").trigger("reset");
|
||||
$("#avatar_upload_overwrite").val(avatarId);
|
||||
$("#avatar_upload_file").trigger('click');
|
||||
});
|
||||
$("#avatar_upload_file").on("change", uploadUserAvatar);
|
||||
|
||||
$(document).on("click", ".bg_example", async function () {
|
||||
//when user clicks on a BG thumbnail...
|
||||
@ -7280,6 +7420,8 @@ $(document).ready(function () {
|
||||
setUserName($('#your_name').val());
|
||||
});
|
||||
|
||||
$("#create_dummy_persona").on('click', createDummyPersona);
|
||||
|
||||
$('#sync_name_button').on('click', async function () {
|
||||
const confirmation = await callPopup(`<h3>Are you sure?</h3>All user-sent messages in this chat will be attributed to ${name1}.`, 'confirm');
|
||||
|
||||
@ -7323,8 +7465,9 @@ $(document).ready(function () {
|
||||
$(document).on('click', '.bind_user_name', bindUserNameToPersona);
|
||||
$(document).on('click', '.delete_avatar', deleteUserAvatar);
|
||||
$(document).on('click', '.set_default_persona', setDefaultPersona);
|
||||
$(document).on('click', '.set_user_info', setUserInfo);
|
||||
$('#lock_user_name').on('click', lockUserNameToChat);
|
||||
$('#persona_description').on('input', onPersonaDescriptionInput);
|
||||
$('#persona_description_position').on('input', onPersonaDescriptionPositionInput);
|
||||
|
||||
//**************************CHARACTER IMPORT EXPORT*************************//
|
||||
$("#character_import_button").click(function () {
|
||||
|
@ -10,7 +10,7 @@ import { selected_group } from "../../group-chats.js";
|
||||
import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
import { getCharaFilename, debounce } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
export { MODULE_NAME as NOTE_MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
@ -222,6 +222,8 @@ function loadSettings() {
|
||||
export function setFloatingPrompt() {
|
||||
const context = getContext();
|
||||
if (!context.groupId && context.characterId === undefined) {
|
||||
console.debug('setFloatingPrompt: Not in a chat. Skipping.');
|
||||
shouldWIAddPrompt = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -243,6 +245,7 @@ export function setFloatingPrompt() {
|
||||
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
|
||||
context.setExtensionPrompt(MODULE_NAME, '');
|
||||
$('#extension_floating_counter').text('(disabled)');
|
||||
shouldWIAddPrompt = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,13 @@ const send_on_enter_options = {
|
||||
ENABLED: 1,
|
||||
}
|
||||
|
||||
export const persona_description_positions = {
|
||||
BEFORE_CHAR: 0,
|
||||
AFTER_CHAR: 1,
|
||||
TOP_AN: 2,
|
||||
BOTTOM_AN: 3,
|
||||
}
|
||||
|
||||
let power_user = {
|
||||
tokenizer: tokenizers.CLASSIC,
|
||||
token_padding: 64,
|
||||
@ -158,6 +165,10 @@ let power_user = {
|
||||
|
||||
personas: {},
|
||||
default_persona: null,
|
||||
persona_descriptions: {},
|
||||
|
||||
persona_description: '',
|
||||
persona_description_position: persona_description_positions.BEFORE_CHAR,
|
||||
};
|
||||
|
||||
let themes = [];
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters } from "../script.js";
|
||||
import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, delay } from "./utils.js";
|
||||
import { getContext } from "./extensions.js";
|
||||
import { metadata_keys, shouldWIAddPrompt } from "./extensions/floating-prompt/index.js";
|
||||
import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./extensions/floating-prompt/index.js";
|
||||
import { registerSlashCommand } from "./slash-commands.js";
|
||||
import { deviceInfo } from "./RossAscends-mods.js";
|
||||
|
||||
@ -867,9 +867,9 @@ async function checkWorldInfo(chat, maxContext) {
|
||||
});
|
||||
|
||||
if (shouldWIAddPrompt) {
|
||||
const originalAN = context.extensionPrompts['2_floating_prompt'].value;
|
||||
const originalAN = context.extensionPrompts[NOTE_MODULE_NAME].value;
|
||||
const ANWithWI = `\n${ANTopInjection.join("\n")}\n${originalAN}\n${ANBottomInjection.reverse().join("\n")}`
|
||||
context.setExtensionPrompt('2_floating_prompt', ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
|
||||
context.setExtensionPrompt(NOTE_MODULE_NAME, ANWithWI, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]);
|
||||
}
|
||||
|
||||
return { worldInfoBefore, worldInfoAfter };
|
||||
|
@ -3734,6 +3734,13 @@ label[for="extensions_autoconnect"] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.menu_button_icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/*------------ TOP SIDE SETTINGS ----------------*/
|
||||
|
||||
#top-settings-holder {
|
||||
@ -3743,10 +3750,10 @@ label[for="extensions_autoconnect"] {
|
||||
max-width: var(--sheldWidth);
|
||||
justify-content: center;
|
||||
display: grid;
|
||||
grid-template-columns: 10% 10% 10% 10% 10% 10% 10% 10%;
|
||||
grid-template-columns: 10% 10% 10% 10% 10% 10% 10% 10% 10%;
|
||||
z-index: 3000;
|
||||
position: relative;
|
||||
grid-gap: 2%;
|
||||
grid-gap: 1%;
|
||||
|
||||
}
|
||||
|
||||
@ -3824,6 +3831,10 @@ label[for="extensions_autoconnect"] {
|
||||
flex-basis: calc((var(--sheldWidth) / 4) - 16px);
|
||||
}
|
||||
|
||||
.drawer33pWidth {
|
||||
flex-basis: calc((var(--sheldWidth) / 3) - 16px);
|
||||
}
|
||||
|
||||
.drawer-content {
|
||||
background-color: var(--SmartThemeBlurTintColor);
|
||||
color: var(--SmartThemeBodyColor);
|
||||
@ -4409,7 +4420,7 @@ body.waifuMode #avatar_zoom_popup {
|
||||
aspect-ratio: 2 / 3;
|
||||
}
|
||||
|
||||
.world_entry_thin_controls {
|
||||
.world_entry_thin_controls, #persona-management-block {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@ -4596,6 +4607,10 @@ body.waifuMode #avatar_zoom_popup {
|
||||
flex-basis: max(calc(100% / 4 - 10px), 190px);
|
||||
}
|
||||
|
||||
.drawer33pWidth {
|
||||
flex-basis: max(calc(100% / 3 - 10px), 190px);
|
||||
}
|
||||
|
||||
.expression-holder {
|
||||
display: none;
|
||||
}
|
||||
@ -4663,6 +4678,9 @@ body.waifuMode #avatar_zoom_popup {
|
||||
.drawer25pWidth {
|
||||
flex-basis: max(calc(100% / 2 - 10px), 180px);
|
||||
}
|
||||
.drawer33pWidth {
|
||||
flex-basis: max(calc(100% / 2 - 10px), 180px);
|
||||
}
|
||||
}
|
||||
|
||||
/*this part only only applies to iOS devices*/
|
||||
@ -4736,4 +4754,4 @@ body.waifuMode #avatar_zoom_popup {
|
||||
#horde_model {
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2223,7 +2223,7 @@ app.post('/uploaduseravatar', urlencodedParser, async (request, response) => {
|
||||
|
||||
const image = await rawImg.cover(AVATAR_WIDTH, AVATAR_HEIGHT).getBufferAsync(jimp.MIME_PNG);
|
||||
|
||||
const filename = `${Date.now()}.png`;
|
||||
const filename = request.body.overwrite_name ?? `${Date.now()}.png`;
|
||||
const pathToNewFile = path.join(directories.avatars, filename);
|
||||
fs.writeFileSync(pathToNewFile, image);
|
||||
fs.rmSync(pathToUpload);
|
||||
|
Reference in New Issue
Block a user