mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' of https://github.com/SillyTavern/SillyTavern into staging
This commit is contained in:
@ -18,6 +18,8 @@ import {
|
||||
getThumbnailUrl,
|
||||
selectCharacterById,
|
||||
eventSource,
|
||||
menu_type,
|
||||
substituteParams,
|
||||
} from "../script.js";
|
||||
|
||||
import {
|
||||
@ -234,7 +236,9 @@ export function RA_CountCharTokens() {
|
||||
total_tokens += Number(counter.text());
|
||||
permanent_tokens += isPermanent ? Number(counter.text()) : 0;
|
||||
} else {
|
||||
const tokens = getTokenCount(value);
|
||||
// We substitute macro for existing characters, but not for the character being created
|
||||
const valueToCount = menu_type === 'create' ? value : substituteParams(value);
|
||||
const tokens = getTokenCount(valueToCount);
|
||||
counter.text(tokens);
|
||||
total_tokens += tokens;
|
||||
permanent_tokens += isPermanent ? tokens : 0;
|
||||
|
@ -3,14 +3,24 @@ import { saveMetadataDebounced } from "./extensions.js";
|
||||
import { registerSlashCommand } from "./slash-commands.js";
|
||||
import { stringFormat } from "./utils.js";
|
||||
|
||||
const METADATA_KEY = 'custom_background';
|
||||
const BG_METADATA_KEY = 'custom_background';
|
||||
const LIST_METADATA_KEY = 'chat_backgrounds';
|
||||
|
||||
/**
|
||||
* @param {string} background
|
||||
* Sets the background for the current chat and adds it to the list of custom backgrounds.
|
||||
* @param {{url: string, path:string}} backgroundInfo
|
||||
*/
|
||||
function forceSetBackground(background) {
|
||||
saveBackgroundMetadata(background);
|
||||
function forceSetBackground(backgroundInfo) {
|
||||
saveBackgroundMetadata(backgroundInfo.url);
|
||||
setCustomBackground();
|
||||
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const bg = backgroundInfo.path;
|
||||
list.push(bg);
|
||||
chat_metadata[LIST_METADATA_KEY] = list;
|
||||
saveMetadataDebounced();
|
||||
getChatBackgroundsList();
|
||||
highlightNewBackground(bg);
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
@ -22,9 +32,27 @@ async function onChatChanged() {
|
||||
unsetCustomBackground();
|
||||
}
|
||||
|
||||
getChatBackgroundsList();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function getChatBackgroundsList() {
|
||||
const list = chat_metadata[LIST_METADATA_KEY];
|
||||
const listEmpty = !Array.isArray(list) || list.length === 0;
|
||||
|
||||
$('#bg_custom_content').empty();
|
||||
$('#bg_chat_hint').toggle(listEmpty);
|
||||
|
||||
if (listEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const bg of list) {
|
||||
const template = getBackgroundFromTemplate(bg, true);
|
||||
$('#bg_custom_content').append(template);
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundPath(fileUrl) {
|
||||
return `backgrounds/${fileUrl}`;
|
||||
}
|
||||
@ -32,7 +60,7 @@ function getBackgroundPath(fileUrl) {
|
||||
function highlightLockedBackground() {
|
||||
$('.bg_example').removeClass('locked');
|
||||
|
||||
const lockedBackground = chat_metadata[METADATA_KEY];
|
||||
const lockedBackground = chat_metadata[BG_METADATA_KEY];
|
||||
|
||||
if (!lockedBackground) {
|
||||
return;
|
||||
@ -71,21 +99,21 @@ function onUnlockBackgroundClick(e) {
|
||||
}
|
||||
|
||||
function hasCustomBackground() {
|
||||
return chat_metadata[METADATA_KEY];
|
||||
return chat_metadata[BG_METADATA_KEY];
|
||||
}
|
||||
|
||||
function saveBackgroundMetadata(file) {
|
||||
chat_metadata[METADATA_KEY] = file;
|
||||
chat_metadata[BG_METADATA_KEY] = file;
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function removeBackgroundMetadata() {
|
||||
delete chat_metadata[METADATA_KEY];
|
||||
delete chat_metadata[BG_METADATA_KEY];
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function setCustomBackground() {
|
||||
const file = chat_metadata[METADATA_KEY];
|
||||
const file = chat_metadata[BG_METADATA_KEY];
|
||||
|
||||
// bg already set
|
||||
if (document.getElementById("bg_custom").style.backgroundImage == file) {
|
||||
@ -100,6 +128,7 @@ function unsetCustomBackground() {
|
||||
}
|
||||
|
||||
function onSelectBackgroundClick() {
|
||||
const isCustom = $(this).attr('custom') === 'true';
|
||||
const relativeBgImage = getUrlParameter(this);
|
||||
|
||||
// if clicked on upload button
|
||||
@ -107,15 +136,18 @@ function onSelectBackgroundClick() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasCustomBackground()) {
|
||||
// Automatically lock the background if it's custom or other background is locked
|
||||
if (hasCustomBackground() || isCustom) {
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
highlightLockedBackground();
|
||||
} else {
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
highlightLockedBackground();
|
||||
const customBg = window.getComputedStyle(document.getElementById('bg_custom')).backgroundImage;
|
||||
|
||||
// custom background is set. Do not override the layer below
|
||||
// Custom background is set. Do not override the layer below
|
||||
if (customBg !== 'none') {
|
||||
return;
|
||||
}
|
||||
@ -123,7 +155,7 @@ function onSelectBackgroundClick() {
|
||||
const bgFile = $(this).attr("bgfile");
|
||||
const backgroundUrl = getBackgroundPath(bgFile);
|
||||
|
||||
// fetching to browser memory to reduce flicker
|
||||
// Fetching to browser memory to reduce flicker
|
||||
fetch(backgroundUrl).then(() => {
|
||||
$("#bg1").css("background-image", relativeBgImage);
|
||||
setBackground(bgFile);
|
||||
@ -132,32 +164,80 @@ function onSelectBackgroundClick() {
|
||||
});
|
||||
}
|
||||
|
||||
async function onRenameBackgroundClick(e) {
|
||||
async function onCopyToSystemBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
const old_bg = $(this).closest('.bg_example').attr('bgfile');
|
||||
const bgNames = await getNewBackgroundName(this);
|
||||
|
||||
if (!old_bg) {
|
||||
if (!bgNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bgFile = await fetch(bgNames.oldBg);
|
||||
|
||||
if (!bgFile.ok) {
|
||||
toastr.warning('Failed to copy background');
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await bgFile.blob();
|
||||
const file = new File([blob], bgNames.newBg);
|
||||
const formData = new FormData();
|
||||
formData.set('avatar', file);
|
||||
|
||||
uploadBackground(formData);
|
||||
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const index = list.indexOf(bgNames.oldBg);
|
||||
list.splice(index, 1);
|
||||
saveMetadataDebounced();
|
||||
getChatBackgroundsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new background name from the user.
|
||||
* @param {Element} referenceElement
|
||||
* @returns {Promise<{oldBg: string, newBg: string}>}
|
||||
* */
|
||||
async function getNewBackgroundName(referenceElement) {
|
||||
const exampleBlock = $(referenceElement).closest('.bg_example');
|
||||
const isCustom = exampleBlock.attr('custom') === 'true';
|
||||
const oldBg = exampleBlock.attr('bgfile');
|
||||
|
||||
if (!oldBg) {
|
||||
console.debug('no bgfile');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileExtension = old_bg.split('.').pop();
|
||||
const old_bg_extensionless = old_bg.replace(`.${fileExtension}`, '');
|
||||
const new_bg_extensionless = await callPopup('<h3>Enter new background name:</h3>', 'input', old_bg_extensionless);
|
||||
const fileExtension = oldBg.split('.').pop();
|
||||
const fileNameBase = isCustom ? oldBg.split('/').pop() : oldBg;
|
||||
const oldBgExtensionless = fileNameBase.replace(`.${fileExtension}`, '');
|
||||
const newBgExtensionless = await callPopup('<h3>Enter new background name:</h3>', 'input', oldBgExtensionless);
|
||||
|
||||
if (!new_bg_extensionless) {
|
||||
if (!newBgExtensionless) {
|
||||
console.debug('no new_bg_extensionless');
|
||||
return;
|
||||
}
|
||||
|
||||
const new_bg = `${new_bg_extensionless}.${fileExtension}`;
|
||||
const newBg = `${newBgExtensionless}.${fileExtension}`;
|
||||
|
||||
if (old_bg_extensionless === new_bg_extensionless) {
|
||||
if (oldBgExtensionless === newBgExtensionless) {
|
||||
console.debug('new_bg === old_bg');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = { old_bg, new_bg };
|
||||
return { oldBg, newBg };
|
||||
}
|
||||
|
||||
async function onRenameBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
const bgNames = await getNewBackgroundName(this);
|
||||
|
||||
if (!bgNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = { old_bg: bgNames.oldBg, new_bg: bgNames.newBg };
|
||||
const response = await fetch('/renamebackground', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
@ -167,7 +247,7 @@ async function onRenameBackgroundClick(e) {
|
||||
|
||||
if (response.ok) {
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(new_bg);
|
||||
highlightNewBackground(bgNames.newBg);
|
||||
} else {
|
||||
toastr.warning('Failed to rename background');
|
||||
}
|
||||
@ -177,28 +257,45 @@ async function onDeleteBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
const bgToDelete = $(this).closest('.bg_example');
|
||||
const url = bgToDelete.data('url');
|
||||
const isCustom = bgToDelete.attr('custom') === 'true';
|
||||
const confirm = await callPopup("<h3>Delete the background?</h3>", 'confirm');
|
||||
const bg = bgToDelete.attr('bgfile');
|
||||
|
||||
if (confirm) {
|
||||
delBackground(bgToDelete.attr("bgfile"));
|
||||
// If it's not custom, it's a built-in background. Delete it from the server
|
||||
if (!isCustom) {
|
||||
delBackground(bg);
|
||||
} else {
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const index = list.indexOf(bg);
|
||||
list.splice(index, 1);
|
||||
}
|
||||
|
||||
const siblingSelector = '.bg_example:not(#form_bg_download)';
|
||||
const nextBg = bgToDelete.next(siblingSelector);
|
||||
const prevBg = bgToDelete.prev(siblingSelector);
|
||||
const anyBg = $(siblingSelector);
|
||||
|
||||
if (nextBg.length > 0) {
|
||||
nextBg.trigger('click');
|
||||
} else {
|
||||
} else if (prevBg.length > 0) {
|
||||
prevBg.trigger('click');
|
||||
} else {
|
||||
$(anyBg[Math.floor(Math.random() * anyBg.length)]).trigger('click');
|
||||
}
|
||||
|
||||
bgToDelete.remove();
|
||||
|
||||
if (url === chat_metadata[METADATA_KEY]) {
|
||||
if (url === chat_metadata[BG_METADATA_KEY]) {
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
if (isCustom) {
|
||||
getChatBackgroundsList();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,18 +356,20 @@ function getUrlParameter(block) {
|
||||
* Instantiates a background template
|
||||
* @param {string} bg Path to background
|
||||
* @param {boolean} isCustom Whether the background is custom
|
||||
* @returns
|
||||
* @returns {JQuery<HTMLElement>} Background template
|
||||
*/
|
||||
function getBackgroundFromTemplate(bg, isCustom) {
|
||||
const thumbPath = getThumbnailUrl('bg', bg);
|
||||
const template = $('#background_template .bg_example').clone();
|
||||
const url = isCustom ? `url("${bg}")` : `url("${getBackgroundPath(bg)}")`;
|
||||
template.attr('title', bg);
|
||||
const thumbPath = isCustom ? bg : getThumbnailUrl('bg', bg);
|
||||
const url = isCustom ? `url("${encodeURI(bg)}")` : `url("${getBackgroundPath(bg)}")`;
|
||||
const title = isCustom ? bg.split('/').pop() : bg;
|
||||
const friendlyTitle = title.slice(0, title.lastIndexOf('.'));
|
||||
template.attr('title', title);
|
||||
template.attr('bgfile', bg);
|
||||
template.attr('custom', String(isCustom));
|
||||
template.data('url', url);
|
||||
template.css('background-image', `url('${thumbPath}')`);
|
||||
template.find('.BGSampleTitle').text(bg.slice(0, bg.lastIndexOf('.')));
|
||||
template.find('.BGSampleTitle').text(friendlyTitle);
|
||||
return template;
|
||||
}
|
||||
|
||||
@ -307,49 +406,43 @@ async function delBackground(bg) {
|
||||
}
|
||||
|
||||
function onBackgroundUploadSelected() {
|
||||
const input = this;
|
||||
const form = $("#form_bg_download").get(0);
|
||||
|
||||
if (input.files && input.files[0]) {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function (e) {
|
||||
const form = $("#form_bg_download").get(0);
|
||||
|
||||
if (!(form instanceof HTMLFormElement)) {
|
||||
console.error('form_bg_download is not a form');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
|
||||
//console.log(formData);
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: "/downloadbackground",
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (bg) {
|
||||
form.reset();
|
||||
setBackground(bg);
|
||||
$("#bg1").css("background-image", `url("${getBackgroundPath(bg)}"`);
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(bg);
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
form.reset();
|
||||
console.log(exception);
|
||||
console.log(jqXHR);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
reader.readAsDataURL(input.files[0]);
|
||||
if (!(form instanceof HTMLFormElement)) {
|
||||
console.error('form_bg_download is not a form');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
uploadBackground(formData);
|
||||
form.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a background to the server
|
||||
* @param {FormData} formData
|
||||
*/
|
||||
function uploadBackground(formData) {
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: "/downloadbackground",
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (bg) {
|
||||
setBackground(bg);
|
||||
$("#bg1").css("background-image", `url("${getBackgroundPath(bg)}"`);
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(bg);
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
console.log(exception);
|
||||
console.log(jqXHR);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -378,11 +471,12 @@ function onBackgroundFilterInput() {
|
||||
export function initBackgrounds() {
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
|
||||
$(document).on("click", ".bg_example", onSelectBackgroundClick);
|
||||
$(document).on("click", '.bg_example', onSelectBackgroundClick);
|
||||
$(document).on('click', '.bg_example_lock', onLockBackgroundClick);
|
||||
$(document).on('click', '.bg_example_unlock', onUnlockBackgroundClick);
|
||||
$(document).on('click', '.bg_example_edit', onRenameBackgroundClick);
|
||||
$(document).on("click", ".bg_example_cross", onDeleteBackgroundClick);
|
||||
$(document).on("click", '.bg_example_cross', onDeleteBackgroundClick);
|
||||
$(document).on("click", '.bg_example_copy', onCopyToSystemBackgroundClick);
|
||||
$('#auto_background').on("click", autoBackgroundCommand);
|
||||
$("#add_bg_button").on('change', onBackgroundUploadSelected);
|
||||
$("#bg-filter").on("input", onBackgroundFilterInput);
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { saveSettingsDebounced, callPopup, getRequestHeaders, substituteParams } from "../../../script.js";
|
||||
import { getContext, extension_settings } from "../../extensions.js";
|
||||
import { initScrollHeight, resetScrollHeight } from "../../utils.js";
|
||||
import { executeSlashCommands, getSlashCommandsHelp, registerSlashCommand } from "../../slash-commands.js";
|
||||
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
|
||||
export { MODULE_NAME };
|
||||
|
||||
@ -15,8 +14,9 @@ const defaultSettings = {
|
||||
quickReplyEnabled: false,
|
||||
numberOfSlots: 5,
|
||||
quickReplySlots: [],
|
||||
placeBeforePromptEnabled: false,
|
||||
placeBeforeInputEnabled: false,
|
||||
quickActionEnabled: false,
|
||||
AutoInputInject: true,
|
||||
}
|
||||
|
||||
//method from worldinfo
|
||||
@ -35,8 +35,12 @@ async function updateQuickReplyPresetList() {
|
||||
|
||||
|
||||
if (presets !== undefined) {
|
||||
presets.forEach((item, i) => {
|
||||
$("#quickReplyPresets").append(`<option value='${item.name}'${selected_preset.includes(item.name) ? ' selected' : ''}>${item.name}</option>`);
|
||||
presets.forEach((item) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = item.name;
|
||||
option.innerText = item.name;
|
||||
option.selected = selected_preset.includes(item.name);
|
||||
$("#quickReplyPresets").append(option);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -50,6 +54,10 @@ async function loadSettings(type) {
|
||||
Object.assign(extension_settings.quickReply, defaultSettings);
|
||||
}
|
||||
|
||||
if (extension_settings.quickReply.AutoInputInject === undefined) {
|
||||
extension_settings.quickReply.AutoInputInject = true;
|
||||
}
|
||||
|
||||
// If the user has an old version of the extension, update it
|
||||
if (!Array.isArray(extension_settings.quickReply.quickReplySlots)) {
|
||||
extension_settings.quickReply.quickReplySlots = [];
|
||||
@ -77,20 +85,21 @@ async function loadSettings(type) {
|
||||
|
||||
$('#quickReplyEnabled').prop('checked', extension_settings.quickReply.quickReplyEnabled);
|
||||
$('#quickReplyNumberOfSlots').val(extension_settings.quickReply.numberOfSlots);
|
||||
$('#placeBeforePromptEnabled').prop('checked', extension_settings.quickReply.placeBeforePromptEnabled);
|
||||
$('#placeBeforeInputEnabled').prop('checked', extension_settings.quickReply.placeBeforeInputEnabled);
|
||||
$('#quickActionEnabled').prop('checked', extension_settings.quickReply.quickActionEnabled);
|
||||
$('#AutoInputInject').prop('checked', extension_settings.quickReply.AutoInputInject);
|
||||
}
|
||||
|
||||
function onQuickReplyInput(id) {
|
||||
extension_settings.quickReply.quickReplySlots[id - 1].mes = $(`#quickReply${id}Mes`).val();
|
||||
$(`#quickReply${id}`).attr('title', ($(`#quickReply${id}Mes`).val()));
|
||||
$(`#quickReply${id}`).attr('title', String($(`#quickReply${id}Mes`).val()));
|
||||
resetScrollHeight($(`#quickReply${id}Mes`));
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onQuickReplyLabelInput(id) {
|
||||
extension_settings.quickReply.quickReplySlots[id - 1].label = $(`#quickReply${id}Label`).val();
|
||||
$(`#quickReply${id}`).text($(`#quickReply${id}Label`).val());
|
||||
$(`#quickReply${id}`).text(String($(`#quickReply${id}Label`).val()));
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
@ -109,8 +118,13 @@ async function onQuickActionEnabledInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onPlaceBeforePromptEnabledInput() {
|
||||
extension_settings.quickReply.placeBeforePromptEnabled = !!$(this).prop('checked');
|
||||
async function onPlaceBeforeInputEnabledInput() {
|
||||
extension_settings.quickReply.placeBeforeInputEnabled = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onAutoInputInject() {
|
||||
extension_settings.quickReply.AutoInputInject = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
@ -125,16 +139,15 @@ async function sendQuickReply(index) {
|
||||
|
||||
let newText;
|
||||
|
||||
if (existingText) {
|
||||
// If existing text, add space after prompt
|
||||
if (extension_settings.quickReply.placeBeforePromptEnabled) {
|
||||
if (existingText && extension_settings.quickReply.AutoInputInject) {
|
||||
if (extension_settings.quickReply.placeBeforeInputEnabled) {
|
||||
newText = `${prompt} ${existingText} `;
|
||||
} else {
|
||||
newText = `${existingText} ${prompt} `;
|
||||
}
|
||||
} else {
|
||||
// If no existing text, add prompt only (with a trailing space)
|
||||
newText = prompt + ' ';
|
||||
// If no existing text and placeBeforeInputEnabled false, add prompt only (with a trailing space)
|
||||
newText = `${prompt} `;
|
||||
}
|
||||
|
||||
newText = substituteParams(newText);
|
||||
@ -142,9 +155,9 @@ async function sendQuickReply(index) {
|
||||
$("#send_textarea").val(newText);
|
||||
|
||||
// Set the focus back to the textarea
|
||||
$("#send_textarea").focus();
|
||||
$("#send_textarea").trigger('focus');
|
||||
|
||||
// Only trigger send button if quickActionEnabled is not checked or
|
||||
// Only trigger send button if quickActionEnabled is not checked or
|
||||
// the prompt starts with '/'
|
||||
if (!extension_settings.quickReply.quickActionEnabled || prompt.startsWith('/')) {
|
||||
$("#send_but").trigger('click');
|
||||
@ -221,7 +234,7 @@ async function saveQuickReplyPreset() {
|
||||
}
|
||||
else {
|
||||
presets[quickReplyPresetIndex] = quickReplyPreset;
|
||||
$(`#quickReplyPresets option[value="${name}"]`).attr('selected', true);
|
||||
$(`#quickReplyPresets option[value="${name}"]`).prop('selected', true);
|
||||
}
|
||||
saveSettingsDebounced();
|
||||
} else {
|
||||
@ -274,8 +287,8 @@ function generateQuickReplyElements() {
|
||||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
quickReplyHtml += `
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply${i}Mes" placeholder="(custom message here)" class="text_pole widthUnset flex1" rows="2"></textarea>
|
||||
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Button label)">
|
||||
<textarea id="quickReply${i}Mes" placeholder="(Custom message or /command)" class="text_pole widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -309,7 +322,7 @@ async function applyQuickReplyPreset(name) {
|
||||
addQuickReplyBar();
|
||||
moduleWorker();
|
||||
|
||||
$(`#quickReplyPresets option[value="${name}"]`).attr('selected', true);
|
||||
$(`#quickReplyPresets option[value="${name}"]`).prop('selected', true);
|
||||
console.debug('QR Preset applied: ' + name);
|
||||
}
|
||||
|
||||
@ -334,7 +347,6 @@ async function doQR(_, text) {
|
||||
}
|
||||
|
||||
jQuery(async () => {
|
||||
|
||||
moduleWorker();
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
const settingsHtml = `
|
||||
@ -348,20 +360,28 @@ jQuery(async () => {
|
||||
<div>
|
||||
<label class="checkbox_label">
|
||||
<input id="quickReplyEnabled" type="checkbox" />
|
||||
Enable Quick Replies
|
||||
Enable Quick Replies
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input id="quickActionEnabled" type="checkbox" />
|
||||
Disable Send / Insert In User Input
|
||||
Disable Send / Insert In User Input
|
||||
</label>
|
||||
<label class="checkbox_label marginBot10">
|
||||
<input id="placeBeforePromptEnabled" type="checkbox" />
|
||||
Place Quick-reply before the Prompt
|
||||
<input id="placeBeforeInputEnabled" type="checkbox" />
|
||||
Place Quick-reply before the Input
|
||||
</label>
|
||||
<label class="checkbox_label marginBot10">
|
||||
<input id="AutoInputInject" type="checkbox" />
|
||||
Inject user input automatically<br>(If disabled, use {{input}} macro for manual injection)
|
||||
</label>
|
||||
<label for="quickReplyPresets">Quick Reply presets:</label>
|
||||
<div class="flex-container flexnowrap wide100p">
|
||||
<select id="quickReplyPresets" name="quickreply-preset">
|
||||
<select id="quickReplyPresets" name="quickreply-preset" class="flex1 text_pole">
|
||||
</select>
|
||||
<i id="quickReplyPresetSaveButton" class="fa-solid fa-save"></i>
|
||||
<div id="quickReplyPresetSaveButton" class="menu_button menu_button_icon">
|
||||
<div class="fa-solid fa-save"></div>
|
||||
<span>Save</span>
|
||||
</div>
|
||||
</div>
|
||||
<label for="quickReplyNumberOfSlots">Number of slots:</label>
|
||||
</div>
|
||||
@ -379,10 +399,11 @@ jQuery(async () => {
|
||||
</div>`;
|
||||
|
||||
$('#extensions_settings2').append(settingsHtml);
|
||||
|
||||
|
||||
// Add event handler for quickActionEnabled
|
||||
$('#quickActionEnabled').on('input', onQuickActionEnabledInput);
|
||||
$('#placeBeforePromptEnabled').on('input', onPlaceBeforePromptEnabledInput);
|
||||
$('#placeBeforeInputEnabled').on('input', onPlaceBeforeInputEnabledInput);
|
||||
$('#AutoInputInject').on('input', onAutoInputInject);
|
||||
$('#quickReplyEnabled').on('input', onQuickReplyEnabledInput);
|
||||
$('#quickReplyNumberOfSlotsApply').on('click', onQuickReplyNumberOfSlotsInput);
|
||||
$("#quickReplyPresetSaveButton").on('click', saveQuickReplyPreset);
|
||||
@ -392,16 +413,13 @@ jQuery(async () => {
|
||||
extension_settings.quickReplyPreset = quickReplyPresetSelected;
|
||||
applyQuickReplyPreset(quickReplyPresetSelected);
|
||||
saveSettingsDebounced();
|
||||
|
||||
});
|
||||
|
||||
await loadSettings('init');
|
||||
addQuickReplyBar();
|
||||
|
||||
});
|
||||
|
||||
$(document).ready(() => {
|
||||
jQuery(() => {
|
||||
registerSlashCommand('qr', doQR, [], '<span class="monospace">(number)</span> – activates the specified Quick Reply', true, true);
|
||||
registerSlashCommand('qrset', doQRPresetSwitch, [], '<span class="monospace">(name)</span> – swaps to the specified Quick Reply Preset', true, true);
|
||||
|
||||
})
|
||||
|
@ -1116,10 +1116,9 @@ async function generatePicture(_, trigger, message, callback) {
|
||||
extension_settings.sd.width = Math.round(extension_settings.sd.height * 1.8 / 64) * 64;
|
||||
}
|
||||
const callbackOriginal = callback;
|
||||
callback = async function (prompt, base64Image) {
|
||||
const imagePath = base64Image;
|
||||
const imgUrl = `url("${encodeURI(base64Image)}")`;
|
||||
eventSource.emit(event_types.FORCE_SET_BACKGROUND, imgUrl);
|
||||
callback = async function (prompt, imagePath) {
|
||||
const imgUrl = `url("${encodeURI(imagePath)}")`;
|
||||
eventSource.emit(event_types.FORCE_SET_BACKGROUND, { url: imgUrl, path: imagePath });
|
||||
|
||||
if (typeof callbackOriginal === 'function') {
|
||||
callbackOriginal(prompt, imagePath);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { callPopup, main_api } from "../../../script.js";
|
||||
import { getContext } from "../../extensions.js";
|
||||
import { getTokenizerModel } from "../../tokenizers.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
import { getTokenCount, getTokenizerModel } from "../../tokenizers.js";
|
||||
|
||||
async function doTokenCounter() {
|
||||
const selectedTokenizer = main_api == 'openai'
|
||||
@ -29,6 +30,20 @@ async function doTokenCounter() {
|
||||
callPopup(dialog, 'text');
|
||||
}
|
||||
|
||||
function doCount() {
|
||||
// get all of the messages in the chat
|
||||
const context = getContext();
|
||||
const messages = context.chat.filter(x => x.mes && !x.is_system).map(x => x.mes);
|
||||
|
||||
//concat all the messages into a single string
|
||||
const allMessages = messages.join(' ');
|
||||
|
||||
console.debug('All messages:', allMessages);
|
||||
|
||||
//toastr success with the token count of the chat
|
||||
toastr.success(`Token count: ${getTokenCount(allMessages)}`);
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
const buttonHtml = `
|
||||
<div id="token_counter" class="list-group-item flex-container flexGap5">
|
||||
@ -37,4 +52,5 @@ jQuery(() => {
|
||||
</div>`;
|
||||
$('#extensionsMenu').prepend(buttonHtml);
|
||||
$('#token_counter').on('click', doTokenCounter);
|
||||
registerSlashCommand('count', doCount, [], '– counts the number of tokens in the current chat', true, false);
|
||||
});
|
||||
|
@ -399,6 +399,7 @@ function switchMessageActions() {
|
||||
power_user.expand_message_actions = value === null ? false : value == "true";
|
||||
$("body").toggleClass("expandMessageActions", power_user.expand_message_actions);
|
||||
$("#expandMessageActions").prop("checked", power_user.expand_message_actions);
|
||||
$('.extraMesButtons, .extraMesButtonsHint').removeAttr('style');
|
||||
}
|
||||
|
||||
function switchUiMode() {
|
||||
|
@ -3,6 +3,7 @@ System-wide Replacement Macros:
|
||||
<li><tt>{{user}}</tt> - your current Persona username</li>
|
||||
<li><tt>{{char}}</tt> - the Character's name</li>
|
||||
<li><tt>{{input}}</tt> - the user input</li>
|
||||
<li><tt>{{// (note)}}</tt> - you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.</li>
|
||||
<li><tt>{{time}}</tt> - the current time</li>
|
||||
<li><tt>{{date}}</tt> - the current date</li>
|
||||
<li><tt>{{weekday}}</tt> - the current weekday</li>
|
||||
|
@ -870,7 +870,7 @@ export async function saveBase64AsFile(base64Data, characterName, filename = "",
|
||||
// If the response is successful, get the saved image path from the server's response
|
||||
if (response.ok) {
|
||||
const responseData = await response.json();
|
||||
return responseData.path;
|
||||
return responseData.path.replace(/\\/g, '/'); // Replace backslashes with forward slashes
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to upload the image to the server');
|
||||
|
Reference in New Issue
Block a user