mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
fix extension registration
This commit is contained in:
129
public/scripts/extensions/backgrounds/index.js
Normal file
129
public/scripts/extensions/backgrounds/index.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import { getContext } from "../../extensions.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'backgrounds';
|
||||
const METADATA_KEY = 'custom_background';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
|
||||
async function moduleWorker() {
|
||||
if (hasCustomBackground()) {
|
||||
$('#unlock_background').show();
|
||||
$('#lock_background').hide();
|
||||
setCustomBackground();
|
||||
}
|
||||
else {
|
||||
$('#unlock_background').hide();
|
||||
$('#lock_background').show();
|
||||
unsetCustomBackground();
|
||||
}
|
||||
}
|
||||
|
||||
function onLockBackgroundClick() {
|
||||
const bgImage = window.getComputedStyle(document.getElementById('bg1')).backgroundImage;
|
||||
|
||||
// Extract the URL from the CSS string
|
||||
const urlRegex = /url\((['"])?(.*?)\1\)/;
|
||||
const matches = bgImage.match(urlRegex);
|
||||
const url = matches[2];
|
||||
|
||||
// Remove the protocol and host, leaving the relative URL
|
||||
const relativeUrl = new URL(url).pathname;
|
||||
const relativeBgImage = `url("${relativeUrl}")`
|
||||
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
$('#unlock_background').show();
|
||||
$('#lock_background').hide();
|
||||
}
|
||||
|
||||
function onUnlockBackgroundClick() {
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
$('#unlock_background').hide();
|
||||
$('#lock_background').show();
|
||||
}
|
||||
|
||||
function hasCustomBackground() {
|
||||
const context = getContext();
|
||||
return !!context.chatMetadata[METADATA_KEY];
|
||||
}
|
||||
|
||||
function saveBackgroundMetadata(file) {
|
||||
const context = getContext();
|
||||
context.chatMetadata[METADATA_KEY] = file;
|
||||
context.saveChat();
|
||||
}
|
||||
|
||||
function removeBackgroundMetadata() {
|
||||
const context = getContext();
|
||||
delete context.chatMetadata[METADATA_KEY];
|
||||
context.saveChat();
|
||||
}
|
||||
|
||||
function setCustomBackground() {
|
||||
const context = getContext();
|
||||
const file = context.chatMetadata[METADATA_KEY];
|
||||
|
||||
// bg already set
|
||||
if (document.getElementById("bg_custom").style.backgroundImage == file) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#bg_custom").css("background-image", file);
|
||||
$("#custom_bg_preview").css("background-image", file);
|
||||
}
|
||||
|
||||
function unsetCustomBackground() {
|
||||
$("#bg_custom").css("background-image", 'none');
|
||||
$("#custom_bg_preview").css("background-image", 'none');
|
||||
}
|
||||
|
||||
function onSelectBackgroundClick() {
|
||||
const bgfile = $(this).attr("bgfile");
|
||||
|
||||
if (hasCustomBackground()) {
|
||||
saveBackgroundMetadata(`url("backgrounds/${bgfile}")`);
|
||||
setCustomBackground();
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
function addSettings() {
|
||||
const html = `
|
||||
<div class="background_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Character Backgrounds</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div class="background_controls">
|
||||
<div id="lock_background" class="menu_button">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
Lock
|
||||
</div>
|
||||
<div id="unlock_background" class="menu_button">
|
||||
<i class="fa-solid fa-unlock"></i>
|
||||
Unlock
|
||||
</div>
|
||||
<small>
|
||||
Press "Lock" to assign a currently selected background to a character or group chat.<br>
|
||||
Any background image selected while lock is engaged will be saved automatically.
|
||||
</small>
|
||||
</div>
|
||||
<div>Preview</div>
|
||||
<div id="custom_bg_preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(html);
|
||||
$('#lock_background').on('click', onLockBackgroundClick);
|
||||
$('#unlock_background').on('click', onUnlockBackgroundClick);
|
||||
$(document).on("click", ".bg_example", onSelectBackgroundClick);
|
||||
}
|
||||
|
||||
addSettings();
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
});
|
11
public/scripts/extensions/backgrounds/manifest.json
Normal file
11
public/scripts/extensions/backgrounds/manifest.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"display_name": "Character Backgrounds",
|
||||
"loading_order": 7,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/Cohee1207/SillyTavern"
|
||||
}
|
45
public/scripts/extensions/backgrounds/style.css
Normal file
45
public/scripts/extensions/backgrounds/style.css
Normal file
@@ -0,0 +1,45 @@
|
||||
#custom_bg_preview {
|
||||
width: 160px;
|
||||
height: 90px;
|
||||
background-color: var(--grey30a);
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
border-radius: 20px;
|
||||
border: 1px solid var(--black50a);
|
||||
box-shadow: 0 0 7px var(--black50a);
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#custom_bg_preview::before {
|
||||
content: 'No Background';
|
||||
color: white;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#custom_bg_preview:not([style*="background-image: none"])::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.background_controls .menu_button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.background_controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.background_controls small {
|
||||
flex-grow: 1;
|
||||
}
|
@@ -16,8 +16,8 @@ async function moduleWorker() {
|
||||
async function setImageIcon() {
|
||||
try {
|
||||
const sendButton = document.getElementById('send_picture');
|
||||
sendButton.style.backgroundImage = `url('/img/image-solid.svg')`;
|
||||
sendButton.classList.remove('spin');
|
||||
sendButton.classList.add('fa-image');
|
||||
sendButton.classList.remove('fa-hourglass-half', 'fa-fade');
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
@@ -27,8 +27,8 @@ async function setImageIcon() {
|
||||
async function setSpinnerIcon() {
|
||||
try {
|
||||
const sendButton = document.getElementById('send_picture');
|
||||
sendButton.style.backgroundImage = `url('/img/spinner-solid.svg')`;
|
||||
sendButton.classList.add('spin');
|
||||
sendButton.classList.remove('fa-image');
|
||||
sendButton.classList.add('fa-hourglass-half', 'fa-fade');
|
||||
}
|
||||
catch (error) {
|
||||
console.log(error);
|
||||
@@ -77,7 +77,8 @@ async function onSelectImage(e) {
|
||||
if (apiResult.ok) {
|
||||
const data = await apiResult.json();
|
||||
const caption = data.caption;
|
||||
await sendCaptionedMessage(caption, base64Img);
|
||||
const imageToSave = data.thumbnail ? `data:image/jpeg;base64,${data.thumbnail}` : base64Img;
|
||||
await sendCaptionedMessage(caption, imageToSave);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
@@ -97,9 +98,9 @@ $(document).ready(function () {
|
||||
$('#send_form').css('grid-template-columns', columns.join(' '));
|
||||
}
|
||||
function addSendPictureButton() {
|
||||
const sendButton = document.createElement('input');
|
||||
sendButton.type = 'button';
|
||||
const sendButton = document.createElement('div');
|
||||
sendButton.id = 'send_picture';
|
||||
sendButton.classList.add('fa-solid');
|
||||
$(sendButton).hide();
|
||||
$(sendButton).on('click', () => $('#img_file').click());
|
||||
$('#send_but_sheld').prepend(sendButton);
|
||||
|
@@ -6,5 +6,8 @@
|
||||
],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css"
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/Cohee1207/SillyTavern"
|
||||
}
|
@@ -4,15 +4,14 @@
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
padding: 1px;
|
||||
background: no-repeat;
|
||||
background-size: 26px auto;
|
||||
background-position: center center;
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
filter: invert(1);
|
||||
opacity: 0.5;
|
||||
filter: brightness(0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#send_picture:hover {
|
||||
@@ -22,15 +21,3 @@
|
||||
#img_form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.spin {
|
||||
animation-name: spin;
|
||||
animation-duration: 2000ms;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {transform:rotate(0deg);}
|
||||
to {transform:rotate(360deg);}
|
||||
}
|
@@ -7,13 +7,13 @@ const UPDATE_INTERVAL = 1000;
|
||||
|
||||
function setDiceIcon() {
|
||||
const sendButton = document.getElementById('roll_dice');
|
||||
sendButton.style.backgroundImage = `url(/img/dice-solid.svg)`;
|
||||
/* sendButton.style.backgroundImage = `url(/img/dice-solid.svg)`; */
|
||||
sendButton.classList.remove('spin');
|
||||
}
|
||||
|
||||
async function doDiceRoll() {
|
||||
let value = $(this).data('value');
|
||||
|
||||
|
||||
if (value == 'custom') {
|
||||
value = await callPopup('Enter the dice formula:<br><i>(for example, <tt>2d6</tt>)</i>', 'input');
|
||||
}
|
||||
@@ -29,22 +29,24 @@ async function doDiceRoll() {
|
||||
|
||||
function addDiceRollButton() {
|
||||
const buttonHtml = `
|
||||
<input id="roll_dice" type="button" />
|
||||
<div id="dice_dropdown">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" data-value="d4">d4</li>
|
||||
<li class="list-group-item" data-value="d6">d6</li>
|
||||
<li class="list-group-item" data-value="d8">d8</li>
|
||||
<li class="list-group-item" data-value="d10">d10</li>
|
||||
<li class="list-group-item" data-value="d12">d12</li>
|
||||
<li class="list-group-item" data-value="d20">d20</li>
|
||||
<li class="list-group-item" data-value="d100">d100</li>
|
||||
<li class="list-group-item" data-value="custom">...</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="roll_dice" class="fa-solid fa-dice" /></div>
|
||||
`;
|
||||
const dropdownHtml = `
|
||||
<div id="dice_dropdown">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item" data-value="d4">d4</li>
|
||||
<li class="list-group-item" data-value="d6">d6</li>
|
||||
<li class="list-group-item" data-value="d8">d8</li>
|
||||
<li class="list-group-item" data-value="d10">d10</li>
|
||||
<li class="list-group-item" data-value="d12">d12</li>
|
||||
<li class="list-group-item" data-value="d20">d20</li>
|
||||
<li class="list-group-item" data-value="d100">d100</li>
|
||||
<li class="list-group-item" data-value="custom">...</li>
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
$('#send_but_sheld').prepend(buttonHtml);
|
||||
$(document.body).append(dropdownHtml)
|
||||
$('#dice_dropdown li').on('click', doDiceRoll);
|
||||
const button = $('#roll_dice');
|
||||
const dropdown = $('#dice_dropdown');
|
||||
|
@@ -4,5 +4,8 @@
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css"
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/Cohee1207/SillyTavern"
|
||||
}
|
@@ -4,17 +4,23 @@
|
||||
height: 40px;
|
||||
margin: 0;
|
||||
padding: 1px;
|
||||
background: no-repeat;
|
||||
background-size: 26px auto;
|
||||
background-position: center center;
|
||||
outline: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: 0.3s;
|
||||
filter: invert(1);
|
||||
opacity: 0.5;
|
||||
opacity: 0.7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
|
||||
#roll_dice:hover {
|
||||
opacity: 1;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
#dice_dropdown {
|
||||
z-index: 100;
|
||||
backdrop-filter: blur(--SmartThemeBlurStrength);
|
||||
}
|
@@ -1,14 +1,44 @@
|
||||
import { saveSettingsDebounced } from "../../../script.js";
|
||||
import { saveSettingsDebounced, token } from "../../../script.js";
|
||||
import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'expressions';
|
||||
const UPDATE_INTERVAL = 1000;
|
||||
const DEFAULT_EXPRESSIONS = ['anger', 'fear', 'joy', 'love', 'sadness', 'surprise'];
|
||||
const DEFAULT_EXPRESSIONS = [
|
||||
"admiration",
|
||||
"amusement",
|
||||
"anger",
|
||||
"annoyance",
|
||||
"approval",
|
||||
"caring",
|
||||
"confusion",
|
||||
"curiosity",
|
||||
"desire",
|
||||
"disappointment",
|
||||
"disapproval",
|
||||
"disgust",
|
||||
"embarrassment",
|
||||
"excitement",
|
||||
"fear",
|
||||
"gratitude",
|
||||
"grief",
|
||||
"joy",
|
||||
"love",
|
||||
"nervousness",
|
||||
"optimism",
|
||||
"pride",
|
||||
"realization",
|
||||
"relief",
|
||||
"remorse",
|
||||
"sadness",
|
||||
"surprise",
|
||||
"neutral"
|
||||
];
|
||||
|
||||
let expressionsList = null;
|
||||
let lastCharacter = undefined;
|
||||
let lastMessage = null;
|
||||
let spriteCache = {};
|
||||
let inApiCall = false;
|
||||
|
||||
function onExpressionsShowDefaultInput() {
|
||||
@@ -28,48 +58,75 @@ function onExpressionsShowDefaultInput() {
|
||||
}
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
function getLastCharacterMessage() {
|
||||
const reversedChat = context.chat.slice().reverse();
|
||||
let isWorkerBusy = false;
|
||||
|
||||
for (let mes of reversedChat) {
|
||||
if (mes.is_user || mes.is_system) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return mes.mes;
|
||||
}
|
||||
|
||||
return '';
|
||||
async function moduleWorkerWrapper() {
|
||||
// Don't touch me I'm busy...
|
||||
if (isWorkerBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// I'm free. Let's update!
|
||||
try {
|
||||
isWorkerBusy = true;
|
||||
await moduleWorker();
|
||||
}
|
||||
finally {
|
||||
isWorkerBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
const context = getContext();
|
||||
|
||||
// group chats and non-characters not supported
|
||||
if (context.groupId || !context.characterId) {
|
||||
// non-characters not supported
|
||||
if (!context.groupId && !context.characterId) {
|
||||
removeExpression();
|
||||
return;
|
||||
}
|
||||
|
||||
// character changed
|
||||
if (lastCharacter !== context.characterId) {
|
||||
if (context.groupId !== lastCharacter && context.characterId !== lastCharacter) {
|
||||
removeExpression();
|
||||
validateImages();
|
||||
spriteCache = {};
|
||||
}
|
||||
|
||||
const currentLastMessage = getLastCharacterMessage();
|
||||
|
||||
// character has no expressions or it is not loaded
|
||||
if (Object.keys(spriteCache).length === 0) {
|
||||
await validateImages(currentLastMessage.name);
|
||||
lastCharacter = context.groupId || context.characterId;
|
||||
}
|
||||
|
||||
const offlineMode = $('.expression_settings .offline_mode');
|
||||
if (!modules.includes('classify')) {
|
||||
$('.expression_settings').show();
|
||||
$('.expression_settings .offline_mode').css('display', 'block');
|
||||
lastCharacter = context.characterId;
|
||||
offlineMode.css('display', 'block');
|
||||
lastCharacter = context.groupId || context.characterId;
|
||||
|
||||
if (context.groupId) {
|
||||
await validateImages(currentLastMessage.name, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
$('.expression_settings .offline_mode').css('display', 'none');
|
||||
// force reload expressions list on connect to API
|
||||
if (offlineMode.is(':visible')) {
|
||||
expressionsList = null;
|
||||
spriteCache = {};
|
||||
expressionsList = await getExpressionsList();
|
||||
await validateImages(currentLastMessage.name, true);
|
||||
}
|
||||
|
||||
offlineMode.css('display', 'none');
|
||||
}
|
||||
|
||||
|
||||
// check if last message changed
|
||||
const currentLastMessage = getLastCharacterMessage();
|
||||
if (lastCharacter === context.characterId && lastMessage === currentLastMessage) {
|
||||
if ((lastCharacter === context.characterId || lastCharacter === context.groupId)
|
||||
&& lastMessage === currentLastMessage.mes) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,13 +146,21 @@ async function moduleWorker() {
|
||||
'Content-Type': 'application/json',
|
||||
'Bypass-Tunnel-Reminder': 'bypass',
|
||||
},
|
||||
body: JSON.stringify({ text: currentLastMessage })
|
||||
body: JSON.stringify({ text: currentLastMessage.mes })
|
||||
});
|
||||
|
||||
if (apiResult.ok) {
|
||||
const name = context.groupId ? currentLastMessage.name : context.name2;
|
||||
const force = !!context.groupId;
|
||||
const data = await apiResult.json();
|
||||
const expression = data.classification[0].label;
|
||||
setExpression(context.name2, expression);
|
||||
let expression = data.classification[0].label;
|
||||
|
||||
// Character won't be angry on you for swiping
|
||||
if (currentLastMessage.mes == '...' && expressionsList.includes('joy')) {
|
||||
expression = 'joy';
|
||||
}
|
||||
|
||||
setExpression(name, expression, force);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -104,51 +169,72 @@ async function moduleWorker() {
|
||||
}
|
||||
finally {
|
||||
inApiCall = false;
|
||||
lastCharacter = context.characterId;
|
||||
lastMessage = currentLastMessage;
|
||||
lastCharacter = context.groupId || context.characterId;
|
||||
lastMessage = currentLastMessage.mes;
|
||||
}
|
||||
}
|
||||
|
||||
function getLastCharacterMessage() {
|
||||
const context = getContext();
|
||||
const reversedChat = context.chat.slice().reverse();
|
||||
|
||||
for (let mes of reversedChat) {
|
||||
if (mes.is_user || mes.is_system) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return { mes: mes.mes, name: mes.name };
|
||||
}
|
||||
|
||||
return { mes: '', name: null };
|
||||
}
|
||||
|
||||
function removeExpression() {
|
||||
lastMessage = null;
|
||||
$('img.expression').off('error');
|
||||
$('img.expression').prop('src', '');
|
||||
$('img.expression').removeClass('default');
|
||||
$('.expression_settings').hide();
|
||||
}
|
||||
|
||||
let imagesValidating = false;
|
||||
|
||||
async function validateImages() {
|
||||
if (imagesValidating) {
|
||||
async function validateImages(character, forceRedrawCached) {
|
||||
if (!character) {
|
||||
return;
|
||||
}
|
||||
|
||||
imagesValidating = true;
|
||||
const context = getContext();
|
||||
const labels = await getExpressionsList();
|
||||
|
||||
if (spriteCache[character]) {
|
||||
if (forceRedrawCached && $('#image_list').data('name') !== character) {
|
||||
console.log('force redrawing character sprites list')
|
||||
drawSpritesList(character, labels, spriteCache[character]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const sprites = await getSpritesList(character);
|
||||
let validExpressions = drawSpritesList(character, labels, sprites);
|
||||
spriteCache[character] = validExpressions;
|
||||
}
|
||||
|
||||
function drawSpritesList(character, labels, sprites) {
|
||||
let validExpressions = [];
|
||||
$('.expression_settings').show();
|
||||
$('#image_list').empty();
|
||||
$('#image_list').data('name', character);
|
||||
labels.sort().forEach((item) => {
|
||||
const sprite = sprites.find(x => x.label == item);
|
||||
|
||||
if (!context.characterId) {
|
||||
imagesValidating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const IMAGE_LIST = (await getExpressionsList()).map(x => `${x}.png`);
|
||||
IMAGE_LIST.forEach((item) => {
|
||||
const image = document.createElement('img');
|
||||
image.src = `/characters/${context.name2}/${item}`;
|
||||
image.classList.add('debug-image');
|
||||
image.width = '0px';
|
||||
image.height = '0px';
|
||||
image.onload = function () {
|
||||
$('#image_list').append(getListItem(item, image.src, 'success'));
|
||||
if (sprite) {
|
||||
validExpressions.push(sprite);
|
||||
$('#image_list').append(getListItem(item, sprite.path, 'success'));
|
||||
}
|
||||
image.onerror = function () {
|
||||
else {
|
||||
$('#image_list').append(getListItem(item, '/img/No-Image-Placeholder.svg', 'failure'));
|
||||
}
|
||||
$('#image_list').prepend(image);
|
||||
});
|
||||
imagesValidating = false;
|
||||
return validExpressions;
|
||||
}
|
||||
|
||||
function getListItem(item, imageSrc, textClass) {
|
||||
@@ -160,8 +246,28 @@ function getListItem(item, imageSrc, textClass) {
|
||||
`;
|
||||
}
|
||||
|
||||
async function getSpritesList(name) {
|
||||
console.log('getting sprites list');
|
||||
|
||||
try {
|
||||
const result = await fetch(`/get_sprites?name=${encodeURIComponent(name)}`, {
|
||||
headers: {
|
||||
'X-CSRF-Token': token,
|
||||
}
|
||||
});
|
||||
|
||||
let sprites = result.ok ? (await result.json()) : [];
|
||||
return sprites;
|
||||
}
|
||||
catch (err) {
|
||||
console.log(err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function getExpressionsList() {
|
||||
// get something for offline mode (6 default images)
|
||||
console.log('getting expressions list');
|
||||
// get something for offline mode (default images)
|
||||
if (!modules.includes('classify')) {
|
||||
return DEFAULT_EXPRESSIONS;
|
||||
}
|
||||
@@ -180,6 +286,7 @@ async function getExpressionsList() {
|
||||
});
|
||||
|
||||
if (apiResult.ok) {
|
||||
|
||||
const data = await apiResult.json();
|
||||
expressionsList = data.labels;
|
||||
return expressionsList;
|
||||
@@ -192,23 +299,37 @@ async function getExpressionsList() {
|
||||
}
|
||||
|
||||
async function setExpression(character, expression, force) {
|
||||
const filename = `${expression}.png`;
|
||||
const debugImageStatus = document.querySelector(`#image_list div[id="${filename}"] span`);
|
||||
console.log('entered setExpressions');
|
||||
await validateImages(character);
|
||||
const img = $('img.expression');
|
||||
|
||||
if (force || (debugImageStatus && !debugImageStatus.classList.contains('failure'))) {
|
||||
//console.log('setting expression from character images folder');
|
||||
const imgUrl = `/characters/${character}/${filename}`;
|
||||
$('img.expression').prop('src', imgUrl);
|
||||
$('img.expression').removeClass('default');
|
||||
const sprite = (spriteCache[character] && spriteCache[character].find(x => x.label === expression));
|
||||
console.log('checking for expression images to show..');
|
||||
if (sprite) {
|
||||
console.log('setting expression from character images folder');
|
||||
img.attr('src', sprite.path);
|
||||
img.removeClass('default');
|
||||
img.off('error');
|
||||
img.on('error', function () {
|
||||
$(this).attr('src', '');
|
||||
if (force && extension_settings.expressions.showDefault) {
|
||||
setDefault();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (extension_settings.expressions.showDefault) {
|
||||
//console.log('no character images, trying default expressions');
|
||||
const defImgUrl = `/img/default-expressions/${filename}`;
|
||||
//console.log(defImgUrl);
|
||||
$('img.expression').prop('src', defImgUrl);
|
||||
$('img.expression').addClass('default');
|
||||
setDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function setDefault() {
|
||||
console.log('setting default');
|
||||
const defImgUrl = `/img/default-expressions/${expression}.png`;
|
||||
//console.log(defImgUrl);
|
||||
img.attr('src', defImgUrl);
|
||||
img.addClass('default');
|
||||
}
|
||||
document.getElementById("expression-holder").style.display = '';
|
||||
}
|
||||
|
||||
function onClickExpressionImage() {
|
||||
@@ -217,35 +338,40 @@ function onClickExpressionImage() {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
const expression = $(this).attr('id').replace('.png', '');
|
||||
const expression = $(this).attr('id');
|
||||
const name = getLastCharacterMessage().name;
|
||||
|
||||
if ($(this).find('.failure').length === 0) {
|
||||
setExpression(context.name2, expression, true);
|
||||
setExpression(name, expression, true);
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
function addExpressionImage() {
|
||||
const html = `<div class="expression-holder"><img class="expression"></div>`;
|
||||
const html = `
|
||||
<div id="expression-holder" class="expression-holder" style="display:none;">
|
||||
<div id="expression-holderheader" class="fa-solid fa-grip drag-grabber"></div>
|
||||
<img id="expression-image" class="expression">
|
||||
</div>`;
|
||||
$('body').append(html);
|
||||
}
|
||||
function addSettings() {
|
||||
|
||||
const html = `
|
||||
<div class="expression_settings">
|
||||
<h4>Expression images</h4>
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>View supported images</b>
|
||||
<div class="inline-drawer-icon down"></div>
|
||||
<b>Expression images</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<p class="offline_mode">You are in offline mode. Click on the image below to set the expression.</p>
|
||||
<div id="image_list"></div>
|
||||
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character. Put PNG images with expressions there.</i></p>
|
||||
</div>
|
||||
<p class="hint"><b>Hint:</b> <i>Create new folder in the <b>public/characters/</b> folder and name it as the name of the character.
|
||||
Put images with expressions there. File names should follow the pattern: <tt>[expression_label].[image_format]</tt></i></p>
|
||||
<label for="expressions_show_default"><input id="expressions_show_default" type="checkbox">Show default images (emojis) if missing</label>
|
||||
</div>
|
||||
</div>
|
||||
<label for="expressions_show_default"><input id="expressions_show_default" type="checkbox">Show default images (emojis) if missing</label>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(html);
|
||||
@@ -257,5 +383,5 @@ function onClickExpressionImage() {
|
||||
|
||||
addExpressionImage();
|
||||
addSettings();
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
setInterval(moduleWorkerWrapper, UPDATE_INTERVAL);
|
||||
})();
|
@@ -6,5 +6,8 @@
|
||||
"classify"
|
||||
],
|
||||
"js": "index.js",
|
||||
"css": "style.css"
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/Cohee1207/SillyTavern"
|
||||
}
|
@@ -5,24 +5,29 @@
|
||||
}
|
||||
|
||||
.expression-holder {
|
||||
min-width: 100px;
|
||||
min-height: 100px;
|
||||
max-height: 90vh;
|
||||
max-width: calc((100vw - 800px)/2);
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
text-align: center;
|
||||
max-width: 90vh;
|
||||
width: calc((100vw - var(--sheldWidth)) /2);
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
padding: 0;
|
||||
filter: drop-shadow(2px 2px 2px #51515199);
|
||||
transition: 500ms;
|
||||
z-index: 3;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
img.expression {
|
||||
max-width: 100%;
|
||||
max-height: 90vh;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: bottom;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
img.expression[src=""] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
img.expression.default {
|
||||
|
@@ -51,76 +51,7 @@ function onExtensionFloatingDefaultInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
// TODO Remove in next release
|
||||
function getLocalStorageKeys() {
|
||||
const context = getContext();
|
||||
|
||||
let keySuffix;
|
||||
|
||||
if (context.groupId) {
|
||||
keySuffix = context.groupId;
|
||||
}
|
||||
else if (context.characterId) {
|
||||
keySuffix = `${context.characters[context.characterId].name}_${context.chatId}`;
|
||||
}
|
||||
else {
|
||||
keySuffix = 'undefined';
|
||||
}
|
||||
|
||||
return {
|
||||
prompt: `extensions_floating_prompt_${keySuffix}`,
|
||||
interval: `extensions_floating_interval_${keySuffix}`,
|
||||
depth: `extensions_floating_depth_${keySuffix}`,
|
||||
position: `extensions_floating_position_${keySuffix}`,
|
||||
default: 'extensions_default_note',
|
||||
};
|
||||
}
|
||||
|
||||
function migrateFromLocalStorage() {
|
||||
const keys = getLocalStorageKeys();
|
||||
const defaultNote = localStorage.getItem(keys.default);
|
||||
const prompt = localStorage.getItem(keys.prompt);
|
||||
const interval = localStorage.getItem(keys.interval);
|
||||
const position = localStorage.getItem(keys.position);
|
||||
const depth = localStorage.getItem(keys.depth);
|
||||
|
||||
if (defaultNote !== null) {
|
||||
if (typeof extension_settings.note !== 'object') {
|
||||
extension_settings.note = {};
|
||||
}
|
||||
|
||||
extension_settings.note.default = defaultNote;
|
||||
saveSettingsDebounced();
|
||||
localStorage.removeItem(keys.default);
|
||||
}
|
||||
|
||||
if (chat_metadata) {
|
||||
if (interval !== null) {
|
||||
chat_metadata[metadata_keys.interval] = interval;
|
||||
localStorage.removeItem(keys.interval);
|
||||
}
|
||||
|
||||
if (depth !== null) {
|
||||
chat_metadata[metadata_keys.depth] = depth;
|
||||
localStorage.removeItem(keys.depth);
|
||||
}
|
||||
|
||||
if (position !== null) {
|
||||
chat_metadata[metadata_keys.position] = position;
|
||||
localStorage.removeItem(keys.position);
|
||||
}
|
||||
|
||||
if (prompt !== null) {
|
||||
chat_metadata[metadata_keys.prompt] = prompt;
|
||||
localStorage.removeItem(keys.prompt);
|
||||
saveChatDebounced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadSettings() {
|
||||
migrateFromLocalStorage();
|
||||
chat_metadata[metadata_keys.prompt] = chat_metadata[metadata_keys.prompt] ?? extension_settings.note.default ?? '';
|
||||
chat_metadata[metadata_keys.interval] = chat_metadata[metadata_keys.interval] ?? DEFAULT_INTERVAL;
|
||||
chat_metadata[metadata_keys.position] = chat_metadata[metadata_keys.position] ?? DEFAULT_POSITION;
|
||||
@@ -150,6 +81,7 @@ async function moduleWorker() {
|
||||
}
|
||||
|
||||
if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
|
||||
context.setExtensionPrompt(MODULE_NAME, '');
|
||||
$('#extension_floating_counter').text('No');
|
||||
return;
|
||||
}
|
||||
@@ -163,35 +95,43 @@ async function moduleWorker() {
|
||||
$('#extension_floating_counter').text(shouldAddPrompt ? 'This' : messagesTillInsertion);
|
||||
}
|
||||
|
||||
(function() {
|
||||
(function () {
|
||||
function addExtensionsSettings() {
|
||||
const settingsHtml = `
|
||||
<h4>Author's Note / Character Bias</h4>
|
||||
<div class="floating_prompt_settings">
|
||||
<label for="extension_floating_prompt">Append the following text:</label>
|
||||
<textarea id="extension_floating_prompt" class="text_pole" rows="2"></textarea>
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_position" value="0" />
|
||||
After scenario
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_position" value="1" />
|
||||
In-chat
|
||||
</label>
|
||||
<label for="extension_floating_interval">Every N messages <b>you</b> send (set to 0 to disable):</label>
|
||||
<input id="extension_floating_interval" class="text_pole" type="number" min="0" max="999" />
|
||||
<label for="extension_floating_interval">Insertion depth (for in-chat positioning):</label>
|
||||
<input id="extension_floating_depth" class="text_pole" type="number" min="0" max="99" />
|
||||
<span>Appending to the prompt in next: <span id="extension_floating_counter">No</span> message(s)</span>
|
||||
<br>
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Author's Note / Character Bias</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label for="extension_floating_prompt">Append the following text:</label>
|
||||
<textarea id="extension_floating_prompt" class="text_pole" rows="8"></textarea>
|
||||
<div class="floating_prompt_radio_group">
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_position" value="0" />
|
||||
After scenario
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="extension_floating_position" value="1" />
|
||||
In-chat
|
||||
</label>
|
||||
</div>
|
||||
<label for="extension_floating_interval">Every N messages <b>you</b> send (set to 0 to disable):</label>
|
||||
<input id="extension_floating_interval" class="text_pole" type="number" min="0" max="999" />
|
||||
<label for="extension_floating_interval">Insertion depth (for in-chat positioning):</label>
|
||||
<input id="extension_floating_depth" class="text_pole" type="number" min="0" max="99" />
|
||||
<span>Appending to the prompt in next: <span id="extension_floating_counter">No</span> message(s)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Default note for new chats</b>
|
||||
<div class="inline-drawer-icon down"></div>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label for="extension_floating_default">Default Author's Note</label>
|
||||
<textarea id="extension_floating_default" class="text_pole" rows="3"
|
||||
<textarea id="extension_floating_default" class="text_pole" rows="8"
|
||||
placeholder="Example:\n[Scenario: wacky adventures; Genre: romantic comedy; Style: verbose, creative]"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -4,5 +4,8 @@
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css"
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/Cohee1207/SillyTavern"
|
||||
}
|
@@ -6,4 +6,14 @@
|
||||
#extension_floating_counter {
|
||||
font-weight: 600;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.floating_prompt_settings textarea {
|
||||
font-size: calc(var(--mainFontSize) * 0.9);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.floating_prompt_radio_group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
@@ -26,7 +26,7 @@ const defaultSettings = {
|
||||
shortMemoryStep: 16,
|
||||
longMemoryStep: 8,
|
||||
repetitionPenaltyStep: 0.05,
|
||||
repetitionPenalty: 1.0,
|
||||
repetitionPenalty: 1.2,
|
||||
maxRepetitionPenalty: 2.0,
|
||||
minRepetitionPenalty: 1.0,
|
||||
temperature: 1.0,
|
||||
@@ -34,28 +34,13 @@ const defaultSettings = {
|
||||
maxTemperature: 2.0,
|
||||
temperatureStep: 0.05,
|
||||
lengthPenalty: 1,
|
||||
minLengthPenalty: 0,
|
||||
maxLengthPenalty: 2,
|
||||
lengthPenaltyStep: 0.05,
|
||||
minLengthPenalty: -4,
|
||||
maxLengthPenalty: 4,
|
||||
lengthPenaltyStep: 0.1,
|
||||
memoryFrozen: false,
|
||||
};
|
||||
|
||||
// TODO Delete in next release
|
||||
function migrateFromLocalStorage() {
|
||||
const SETTINGS_KEY = 'extensions_memory_settings';
|
||||
const settings = localStorage.getItem(SETTINGS_KEY);
|
||||
|
||||
if (settings !== null) {
|
||||
const savedSettings = JSON.parse(settings);
|
||||
Object.assign(extension_settings.memory, savedSettings);
|
||||
localStorage.removeItem(SETTINGS_KEY);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
}
|
||||
|
||||
function loadSettings() {
|
||||
migrateFromLocalStorage();
|
||||
|
||||
if (Object.keys(extension_settings.memory).length === 0) {
|
||||
Object.assign(extension_settings.memory, defaultSettings);
|
||||
}
|
||||
@@ -223,14 +208,14 @@ async function summarizeChat(context) {
|
||||
memoryBuffer.push(entry);
|
||||
|
||||
// check if token limit was reached
|
||||
if (context.encode(getMemoryString()).length >= extension_settings.memory.shortMemoryLength) {
|
||||
if (context.getTokenCount(getMemoryString()) >= extension_settings.memory.shortMemoryLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const resultingString = getMemoryString();
|
||||
|
||||
if (context.encode(resultingString).length < extension_settings.memory.shortMemoryLength) {
|
||||
if (context.getTokenCount(resultingString) < extension_settings.memory.shortMemoryLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,7 +234,7 @@ async function summarizeChat(context) {
|
||||
body: JSON.stringify({
|
||||
text: resultingString,
|
||||
params: {
|
||||
min_length: extension_settings.memory.longMemoryLength * 0.8,
|
||||
min_length: extension_settings.memory.longMemoryLength * 0, // testing how it behaves 0 min length
|
||||
max_length: extension_settings.memory.longMemoryLength,
|
||||
repetition_penalty: extension_settings.memory.repetitionPenalty,
|
||||
temperature: extension_settings.memory.temperature,
|
||||
@@ -322,28 +307,35 @@ $(document).ready(function () {
|
||||
function addExtensionControls() {
|
||||
const settingsHtml = `
|
||||
<div id="memory_settings">
|
||||
<h4>Memory</h4>
|
||||
<label for="memory_contents">Memory contents</label>
|
||||
<textarea id="memory_contents" class="text_pole" rows="8" placeholder="Context will be generated here..."></textarea>
|
||||
<div class="memory_contents_controls">
|
||||
<input id="memory_restore" class="menu_button" type="submit" value="Restore previous state" />
|
||||
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" /> Freeze context</label>
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Chat memory</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label for="memory_contents">Memory contents</label>
|
||||
<textarea id="memory_contents" class="text_pole" rows="8" placeholder="Context will be generated here..."></textarea>
|
||||
<div class="memory_contents_controls">
|
||||
<input id="memory_restore" class="menu_button" type="submit" value="Restore previous state" />
|
||||
<label for="memory_frozen"><input id="memory_frozen" type="checkbox" /> Freeze context</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Summarization settings</b>
|
||||
<div class="inline-drawer-icon down"></div>
|
||||
<b>Summarization parameters</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<label for="memory_short_length">Memory summarization [short-term] length (<span id="memory_short_length_tokens"></span> tokens)</label>
|
||||
<label for="memory_short_length">Buffer <small>[short-term]</small> length (<span id="memory_short_length_tokens"></span> tokens)</label>
|
||||
<input id="memory_short_length" type="range" value="${defaultSettings.shortMemoryLength}" min="${defaultSettings.minShortMemory}" max="${defaultSettings.maxShortMemory}" step="${defaultSettings.shortMemoryStep}" />
|
||||
<label for="memory_long_length">Memory context [long-term] length (<span id="memory_long_length_tokens"></span> tokens)</label>
|
||||
<label for="memory_long_length">Summary <small>[long-term]</small> length (<span id="memory_long_length_tokens"></span> tokens)</label>
|
||||
<input id="memory_long_length" type="range" value="${defaultSettings.longMemoryLength}" min="${defaultSettings.minLongMemory}" max="${defaultSettings.maxLongMemory}" step="${defaultSettings.longMemoryStep}" />
|
||||
<label for="memory_temperature">Summarization temperature (<span id="memory_temperature_value"></span>)</label>
|
||||
<label for="memory_temperature">Temperature (<span id="memory_temperature_value"></span>)</label>
|
||||
<input id="memory_temperature" type="range" value="${defaultSettings.temperature}" min="${defaultSettings.minTemperature}" max="${defaultSettings.maxTemperature}" step="${defaultSettings.temperatureStep}" />
|
||||
<label for="memory_repetition_penalty">Summarization repetition penalty (<span id="memory_repetition_penalty_value"></span>)</label>
|
||||
<label for="memory_repetition_penalty">Repetition penalty (<span id="memory_repetition_penalty_value"></span>)</label>
|
||||
<input id="memory_repetition_penalty" type="range" value="${defaultSettings.repetitionPenalty}" min="${defaultSettings.minRepetitionPenalty}" max="${defaultSettings.maxRepetitionPenalty}" step="${defaultSettings.repetitionPenaltyStep}" />
|
||||
<label for="memory_length_penalty">Summarization length penalty (<span id="memory_length_penalty_value"></span>)</label>
|
||||
<label for="memory_length_penalty">Length penalty <small>[higher = longer summaries]</small> (<span id="memory_length_penalty_value"></span>)</label>
|
||||
<input id="memory_length_penalty" type="range" value="${defaultSettings.lengthPenalty}" min="${defaultSettings.minLengthPenalty}" max="${defaultSettings.maxLengthPenalty}" step="${defaultSettings.lengthPenaltyStep}" />
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -6,5 +6,8 @@
|
||||
],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css"
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/Cohee1207/SillyTavern"
|
||||
}
|
@@ -4,7 +4,7 @@
|
||||
}
|
||||
|
||||
#memory_settings textarea {
|
||||
font-size: 14px;
|
||||
font-size: calc(var(--mainFontSize) * 0.9);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user