User persona management block. Persona descriptions. Dummy personas. Change persona avatar

This commit is contained in:
Cohee
2023-06-25 16:46:23 +03:00
parent bbec184d17
commit 26ac519c55
7 changed files with 335 additions and 135 deletions

View File

@ -2001,7 +2001,7 @@
<div id="user-settings-button" class="drawer"> <div id="user-settings-button" class="drawer">
<div class="drawer-toggle"> <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>
<div id="user-settings-block" class="drawer-content closedDrawer"> <div id="user-settings-block" class="drawer-content closedDrawer">
<div class="flex-container wide100p alignitemscenter spaceBetween"> <div class="flex-container wide100p alignitemscenter spaceBetween">
@ -2009,7 +2009,7 @@
<div id="version_display"></div> <div id="version_display"></div>
</div> </div>
<div class="flex-container spaceEvenly"> <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"> <div class="ui-settings">
<h4><span data-i18n="UI Customization">UI Customization</span></h4> <h4><span data-i18n="UI Customization">UI Customization</span></h4>
<div> <div>
@ -2098,13 +2098,25 @@
<span data-i18n="Movable UI Panels">Movable UI Panels</span> <span data-i18n="Movable UI Panels">Movable UI Panels</span>
</label> </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"> <div id="movingUIreset" class="menu_button whitespacenowrap" data-i18n="Reset Panels">
Reset Panels</div> Reset Panels</div>
</div> </div>
</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"> <div id="color-picker-block" class="flex-container flexFlowColumn">
<h4><span data-i18n="UI Colors">UI Colors</span></h4> <h4><span data-i18n="UI Colors">UI Colors</span></h4>
<div class="flex-container"> <div class="flex-container">
@ -2201,7 +2213,7 @@
</div> </div>
</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"> <div id="power-user-option-checkboxes">
<h4 data-i18n="Power User Options">Power User Options</h4> <h4 data-i18n="Power User Options">Power User Options</h4>
<label for="swipes-checkbox"> <label for="swipes-checkbox">
@ -2253,74 +2265,33 @@
<span data-i18n="Never Resize Avatars">Never Resize Avatars</span> <span data-i18n="Never Resize Avatars">Never Resize Avatars</span>
</label> </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"> <div id="reload_chat" class="menu_button whitespacenowrap" data-i18n="Reload Chat">
Reload Chat Reload Chat
</div> </div>
</div> </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> </div>
</div> </div>
@ -2383,6 +2354,60 @@
</div> </div>
</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:&#10;[{{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="rightNavHolder" class="drawer">
<div id="unimportantYes" class="drawer-toggle drawer-header"> <div id="unimportantYes" class="drawer-toggle drawer-header">
<div id="rightNavDrawerIcon" class="drawer-icon fa-solid fa-address-card closedIcon" title="Character Management"> <div id="rightNavDrawerIcon" class="drawer-icon fa-solid fa-address-card closedIcon" title="Character Management">
@ -3369,8 +3394,8 @@
</button> </button>
</div> </div>
<div class="avatar-buttons avatar-buttons-bottom"> <div class="avatar-buttons avatar-buttons-bottom">
<button class="menu_button set_user_info" title="Under construction"> <button class="menu_button set_persona_image" title="Change persona image">
<i class="fa-solid fa-circle-user"></i> <i class="fa-solid fa-image"></i>
</button> </button>
<button class="menu_button delete_avatar" title="Delete persona"> <button class="menu_button delete_avatar" title="Delete persona">

View File

@ -69,6 +69,7 @@ import {
formatInstructModeChat, formatInstructModeChat,
formatInstructStoryString, formatInstructStoryString,
formatInstructModePrompt, formatInstructModePrompt,
persona_description_positions,
} from "./scripts/power-user.js"; } from "./scripts/power-user.js";
import { import {
@ -151,7 +152,7 @@ import {
import { EventEmitter } from './scripts/eventemitter.js'; import { EventEmitter } from './scripts/eventemitter.js';
import { context_settings, loadContextTemplatesFromSettings } from "./scripts/context-template.js"; import { context_settings, loadContextTemplatesFromSettings } from "./scripts/context-template.js";
import { markdownExclusionExt } from "./scripts/showdown-exclusion.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 //exporting functions and vars for mods
export { export {
@ -1554,6 +1555,28 @@ function cleanGroupMessage(getMessage) {
return 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() { function getAllExtensionPrompts() {
const value = Object const value = Object
.values(extension_prompts) .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}`); 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 = ""; let storyString = "";
if (is_pygmalion) { if (is_pygmalion) {
@ -2062,11 +2079,19 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
setFloatingPrompt(); setFloatingPrompt();
// Add WI to prompt (and also inject WI to AN value via hijack) // Add WI to prompt (and also inject WI to AN value via hijack)
let { worldInfoString, worldInfoBefore, worldInfoAfter } = await getWorldInfoPrompt(chat2, this_max_context); let { worldInfoString, worldInfoBefore, worldInfoAfter } = await getWorldInfoPrompt(chat2, this_max_context);
// Add persona description to prompt
storyString = getPersonaDescription(storyString);
// Call combined AN into Generate // Call combined AN into Generate
let allAnchors = getAllExtensionPrompts(); let allAnchors = getAllExtensionPrompts();
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO); const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.AFTER_SCENARIO);
let zeroDepthAnchor = getExtensionPrompt(extension_prompt_types.IN_CHAT, 0, ' '); 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 // Moved here to not overflow the Poe context with added prompt bits
if (main_api == 'poe') { if (main_api == 'poe') {
allAnchors = appendPoeAnchors(type, allAnchors); allAnchors = appendPoeAnchors(type, allAnchors);
@ -3954,8 +3979,6 @@ function changeMainAPI() {
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
async function getUserAvatars() { 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", { const response = await fetch("/getuseravatars", {
method: "POST", method: "POST",
headers: getRequestHeaders(), headers: getRequestHeaders(),
@ -3967,6 +3990,8 @@ async function getUserAvatars() {
const getData = await response.json(); const getData = await response.json();
//background = getData; //background = getData;
//console.log(getData.length); //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++) { for (var i = 0; i < getData.length; i++) {
//console.log(1); //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() { function highlightSelectedAvatar() {
$("#user_avatar_block").find(".avatar").removeClass("selected"); $("#user_avatar_block").find(".avatar").removeClass("selected");
$("#user_avatar_block") $("#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 // If the user clicked ok and entered a name, bind the name to the persona
console.log(`Binding persona ${avatarId} to name ${personaName}`); console.log(`Binding persona ${avatarId} to name ${personaName}`);
power_user.personas[avatarId] = 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 the user is currently using this persona, update the name
if (avatarId === user_avatar) { if (isCurrentPersona) {
console.log(`Auto-updating user name to ${personaName}`); console.log(`Auto-updating user name to ${personaName}`);
setUserName(personaName); setUserName(personaName);
} }
@ -4063,10 +4149,33 @@ async function bindUserNameToPersona() {
// If the user clicked ok, but didn't enter a name, delete the persona // If the user clicked ok, but didn't enter a name, delete the persona
console.log(`Unbinding persona ${avatarId}`); console.log(`Unbinding persona ${avatarId}`);
delete power_user.personas[avatarId]; delete power_user.personas[avatarId];
delete power_user.persona_descriptions[avatarId];
} }
saveSettingsDebounced(); saveSettingsDebounced();
await getUserAvatars(); 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() { function updateUserLockIcon() {
@ -4093,12 +4202,75 @@ function setUserAvatar() {
} }
setUserName(personaName); 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() { async function uploadUserAvatar(e) {
// TODO Replace with actual implementation const file = e.target.files[0];
callPopup('This functionality is under development.<br>Please check back later.', 'text');
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() { async function setDefaultPersona() {
@ -4179,6 +4351,7 @@ async function deleteUserAvatar() {
if (request.ok) { if (request.ok) {
console.log(`Deleted avatar ${avatarId}`); console.log(`Deleted avatar ${avatarId}`);
delete power_user.personas[avatarId]; delete power_user.personas[avatarId];
delete power_user.persona_descriptions[avatarId];
if (avatarId === power_user.default_persona) { 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'); 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, }, { timeOut: 10000, extendedTimeOut: 20000, },
); );
power_user.personas[user_avatar] = name1; power_user.personas[user_avatar] = name1;
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.BEFORE_CHAR };
} }
chat_metadata['persona'] = user_avatar; chat_metadata['persona'] = user_avatar;
@ -4412,6 +4586,7 @@ async function getSettings(type) {
user_avatar = settings.user_avatar; user_avatar = settings.user_avatar;
reloadUserAvatar(); reloadUserAvatar();
highlightSelectedAvatar(); highlightSelectedAvatar();
setPersonaDescription();
//Load the API server URL from settings //Load the API server URL from settings
api_server = settings.api_server; 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", setUserAvatar);
$(document).on("click", "#user_avatar_block .avatar_upload", function () { $(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) { $(document).on("click", "#user_avatar_block .set_persona_image", function () {
const file = e.target.files[0]; const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile');
if (!file) { if (!avatarId) {
console.log('no imgfile');
return; return;
} }
const formData = new FormData($("#form_upload_avatar").get(0)); $("#avatar_upload_overwrite").val(avatarId);
$("#avatar_upload_file").trigger('click');
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_file").on("change", uploadUserAvatar);
$(document).on("click", ".bg_example", async function () { $(document).on("click", ".bg_example", async function () {
//when user clicks on a BG thumbnail... //when user clicks on a BG thumbnail...
@ -7280,6 +7420,8 @@ $(document).ready(function () {
setUserName($('#your_name').val()); setUserName($('#your_name').val());
}); });
$("#create_dummy_persona").on('click', createDummyPersona);
$('#sync_name_button').on('click', async function () { $('#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'); 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', '.bind_user_name', bindUserNameToPersona);
$(document).on('click', '.delete_avatar', deleteUserAvatar); $(document).on('click', '.delete_avatar', deleteUserAvatar);
$(document).on('click', '.set_default_persona', setDefaultPersona); $(document).on('click', '.set_default_persona', setDefaultPersona);
$(document).on('click', '.set_user_info', setUserInfo);
$('#lock_user_name').on('click', lockUserNameToChat); $('#lock_user_name').on('click', lockUserNameToChat);
$('#persona_description').on('input', onPersonaDescriptionInput);
$('#persona_description_position').on('input', onPersonaDescriptionPositionInput);
//**************************CHARACTER IMPORT EXPORT*************************// //**************************CHARACTER IMPORT EXPORT*************************//
$("#character_import_button").click(function () { $("#character_import_button").click(function () {

View File

@ -10,7 +10,7 @@ import { selected_group } from "../../group-chats.js";
import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js"; import { ModuleWorkerWrapper, extension_settings, getContext, saveMetadataDebounced } from "../../extensions.js";
import { registerSlashCommand } from "../../slash-commands.js"; import { registerSlashCommand } from "../../slash-commands.js";
import { getCharaFilename, debounce } from "../../utils.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 MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory
const UPDATE_INTERVAL = 1000; const UPDATE_INTERVAL = 1000;
@ -222,6 +222,8 @@ function loadSettings() {
export function setFloatingPrompt() { export function setFloatingPrompt() {
const context = getContext(); const context = getContext();
if (!context.groupId && context.characterId === undefined) { if (!context.groupId && context.characterId === undefined) {
console.debug('setFloatingPrompt: Not in a chat. Skipping.');
shouldWIAddPrompt = false;
return; return;
} }
@ -243,6 +245,7 @@ export function setFloatingPrompt() {
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) { if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
context.setExtensionPrompt(MODULE_NAME, ''); context.setExtensionPrompt(MODULE_NAME, '');
$('#extension_floating_counter').text('(disabled)'); $('#extension_floating_counter').text('(disabled)');
shouldWIAddPrompt = false;
return; return;
} }

View File

@ -75,6 +75,13 @@ const send_on_enter_options = {
ENABLED: 1, ENABLED: 1,
} }
export const persona_description_positions = {
BEFORE_CHAR: 0,
AFTER_CHAR: 1,
TOP_AN: 2,
BOTTOM_AN: 3,
}
let power_user = { let power_user = {
tokenizer: tokenizers.CLASSIC, tokenizer: tokenizers.CLASSIC,
token_padding: 64, token_padding: 64,
@ -158,6 +165,10 @@ let power_user = {
personas: {}, personas: {},
default_persona: null, default_persona: null,
persona_descriptions: {},
persona_description: '',
persona_description_position: persona_description_positions.BEFORE_CHAR,
}; };
let themes = []; let themes = [];

View File

@ -1,7 +1,7 @@
import { saveSettings, callPopup, substituteParams, getTokenCount, getRequestHeaders, chat_metadata, this_chid, characters } from "../script.js"; 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 { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, delay } from "./utils.js";
import { getContext } from "./extensions.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 { registerSlashCommand } from "./slash-commands.js";
import { deviceInfo } from "./RossAscends-mods.js"; import { deviceInfo } from "./RossAscends-mods.js";
@ -867,9 +867,9 @@ async function checkWorldInfo(chat, maxContext) {
}); });
if (shouldWIAddPrompt) { 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")}` 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 }; return { worldInfoBefore, worldInfoAfter };

View File

@ -3734,6 +3734,13 @@ label[for="extensions_autoconnect"] {
flex-grow: 1; flex-grow: 1;
} }
.menu_button_icon {
display: flex;
align-items: center;
width: fit-content;
gap: 5px;
}
/*------------ TOP SIDE SETTINGS ----------------*/ /*------------ TOP SIDE SETTINGS ----------------*/
#top-settings-holder { #top-settings-holder {
@ -3743,10 +3750,10 @@ label[for="extensions_autoconnect"] {
max-width: var(--sheldWidth); max-width: var(--sheldWidth);
justify-content: center; justify-content: center;
display: grid; 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; z-index: 3000;
position: relative; position: relative;
grid-gap: 2%; grid-gap: 1%;
} }
@ -3824,6 +3831,10 @@ label[for="extensions_autoconnect"] {
flex-basis: calc((var(--sheldWidth) / 4) - 16px); flex-basis: calc((var(--sheldWidth) / 4) - 16px);
} }
.drawer33pWidth {
flex-basis: calc((var(--sheldWidth) / 3) - 16px);
}
.drawer-content { .drawer-content {
background-color: var(--SmartThemeBlurTintColor); background-color: var(--SmartThemeBlurTintColor);
color: var(--SmartThemeBodyColor); color: var(--SmartThemeBodyColor);
@ -4409,7 +4420,7 @@ body.waifuMode #avatar_zoom_popup {
aspect-ratio: 2 / 3; aspect-ratio: 2 / 3;
} }
.world_entry_thin_controls { .world_entry_thin_controls, #persona-management-block {
flex-direction: column; flex-direction: column;
} }
@ -4596,6 +4607,10 @@ body.waifuMode #avatar_zoom_popup {
flex-basis: max(calc(100% / 4 - 10px), 190px); flex-basis: max(calc(100% / 4 - 10px), 190px);
} }
.drawer33pWidth {
flex-basis: max(calc(100% / 3 - 10px), 190px);
}
.expression-holder { .expression-holder {
display: none; display: none;
} }
@ -4663,6 +4678,9 @@ body.waifuMode #avatar_zoom_popup {
.drawer25pWidth { .drawer25pWidth {
flex-basis: max(calc(100% / 2 - 10px), 180px); 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*/ /*this part only only applies to iOS devices*/
@ -4736,4 +4754,4 @@ body.waifuMode #avatar_zoom_popup {
#horde_model { #horde_model {
height: unset; height: unset;
} }
} }

View File

@ -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 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); const pathToNewFile = path.join(directories.avatars, filename);
fs.writeFileSync(pathToNewFile, image); fs.writeFileSync(pathToNewFile, image);
fs.rmSync(pathToUpload); fs.rmSync(pathToUpload);