diff --git a/public/index.html b/public/index.html
index 5e2959124..5547589b6 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6440,6 +6440,9 @@
+
diff --git a/public/scripts/personas.js b/public/scripts/personas.js
index 45f515c5f..ea6952495 100644
--- a/public/scripts/personas.js
+++ b/public/scripts/personas.js
@@ -1001,6 +1001,42 @@ export function retriggerFirstMessageOnEmptyChat() {
}
}
+/**
+ * Duplicates a persona.
+ * @param {string} avatarId
+ * @returns {Promise}
+ */
+async function duplicatePersona(avatarId) {
+ const personaName = power_user.personas[avatarId];
+
+ if (!personaName) {
+ toastr.warning('Current avatar is not a persona');
+ return;
+ }
+
+ const confirm = await Popup.show.confirm('Are you sure you want to duplicate this persona?', personaName);
+
+ if (!confirm) {
+ console.debug('User cancelled duplicating persona');
+ return;
+ }
+
+ const newAvatarId = `${Date.now()}-${personaName.replace(/[^a-zA-Z0-9]/g, '')}.png`;
+ const descriptor = power_user.persona_descriptions[avatarId];
+
+ power_user.personas[newAvatarId] = personaName;
+ power_user.persona_descriptions[newAvatarId] = {
+ description: descriptor?.description || '',
+ position: descriptor?.position || persona_description_positions.IN_PROMPT,
+ depth: descriptor?.depth || DEFAULT_DEPTH,
+ role: descriptor?.role || DEFAULT_ROLE,
+ };
+
+ await uploadUserAvatar(getUserAvatar(avatarId), newAvatarId);
+ await getUserAvatars(true, newAvatarId);
+ saveSettingsDebounced();
+}
+
export function initPersonas() {
$(document).on('click', '.bind_user_name', bindUserNameToPersona);
$(document).on('click', '.set_default_persona', setDefaultPersona);
@@ -1059,6 +1095,18 @@ export function initPersonas() {
$('#avatar_upload_file').trigger('click');
});
+ $(document).on('click', '#user_avatar_block .duplicate_persona', function (e) {
+ e.stopPropagation();
+ const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile');
+
+ if (!avatarId) {
+ console.log('no imgfile');
+ return;
+ }
+
+ duplicatePersona(avatarId);
+ });
+
$(document).on('click', '#user_avatar_block .set_persona_image', function (e) {
e.stopPropagation();
const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile');
diff --git a/public/style.css b/public/style.css
index 95a61cdae..13faa3439 100644
--- a/public/style.css
+++ b/public/style.css
@@ -3032,6 +3032,10 @@ grammarly-extension {
opacity: 1;
}
+.avatar-container .avatar-buttons .menu_button {
+ padding: 3px;
+}
+
/* Ross should be able to handle this later */
/*.big-avatars .avatar-buttons{
justify-content: center;