fix extension registration

This commit is contained in:
ouoertheo
2023-04-21 08:06:28 -05:00
163 changed files with 14951 additions and 3000 deletions

View 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);
});

View 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"
}

View 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;
}

View File

@@ -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);

View File

@@ -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"
}

View File

@@ -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);}
}

View File

@@ -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');

View File

@@ -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"
}

View File

@@ -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);
}

View File

@@ -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);
})();

View File

@@ -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"
}

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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"
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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"
}

View File

@@ -4,7 +4,7 @@
}
#memory_settings textarea {
font-size: 14px;
font-size: calc(var(--mainFontSize) * 0.9);
line-height: 1.2;
}