mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-01-20 13:38:49 +01:00
Merge branch 'SillyTavern:staging' into staging
This commit is contained in:
commit
f106666ded
@ -5,3 +5,4 @@ readme*
|
||||
Start.bat
|
||||
/dist
|
||||
/backups/
|
||||
cloudflared.exe
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,3 +28,4 @@ secrets.json
|
||||
public/movingUI/
|
||||
public/QuickReplies/
|
||||
content.log
|
||||
cloudflared.exe
|
||||
|
18
Remote-Link.cmd
Normal file
18
Remote-Link.cmd
Normal file
@ -0,0 +1,18 @@
|
||||
@echo off
|
||||
echo ========================================================================================================================
|
||||
echo WARNING: Cloudflare Tunnel!
|
||||
echo ========================================================================================================================
|
||||
echo This script downloads and runs the latest cloudflared.exe from Cloudflare to set up an HTTPS tunnel to your SillyTavern!
|
||||
echo Using the randomly generated temporary tunnel URL, anyone can access your SillyTavern over the Internet while the tunnel
|
||||
echo is active. Keep the URL safe and secure your SillyTavern installation by setting a username and password in config.conf!
|
||||
echo.
|
||||
echo See https://docs.sillytavern.app/usage/remoteconnections/ for more details about how to secure your SillyTavern install.
|
||||
echo.
|
||||
echo By continuing you confirm that you're aware of the potential dangers of having a tunnel open and take all responsibility
|
||||
echo to properly use and secure it!
|
||||
echo.
|
||||
echo To abort, press Ctrl+C or close this window now!
|
||||
echo.
|
||||
pause
|
||||
if not exist cloudflared.exe curl -Lo cloudflared.exe https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe
|
||||
cloudflared.exe tunnel --url localhost:8000
|
@ -1555,30 +1555,55 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="textgenerationwebui_api" style="display: none;position: relative;">
|
||||
<div class="flex-container">
|
||||
<a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
|
||||
oobabooga/text-generation-webui
|
||||
</a>
|
||||
<span data-i18n="Make sure you run it with">
|
||||
Make sure you run it with <tt>--api</tt> flag
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<div class="flex1">
|
||||
<h4 data-i18n="Blocking API url">Blocking API url</h4>
|
||||
<small>Example: http://127.0.0.1:5000/</small>
|
||||
<input id="textgenerationwebui_api_url_text" name="textgenerationwebui_api_url" class="text_pole wide100p" maxlength="500" value="" autocomplete="off">
|
||||
</div>
|
||||
<div class="flex1">
|
||||
<h4 data-i18n="Streaming API url">Streaming API url</h4>
|
||||
<small>Example: ws://127.0.0.1:5005/api/v1/stream</small>
|
||||
<input id="streaming_url_textgenerationwebui" type="text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off">
|
||||
<form action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
If you are using:
|
||||
<div class="flex-container indent20p">
|
||||
<a href="https://github.com/oobabooga/text-generation-webui" target="_blank">
|
||||
oobabooga/text-generation-webui
|
||||
</a>,
|
||||
<span data-i18n="Make sure you run it with">
|
||||
Make sure you run it with <tt>--api</tt> flag
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex-container indent20p">
|
||||
<a href="https://mancer.tech/" target="_blank">
|
||||
Mancer AI
|
||||
</a>,
|
||||
<label class="checkbox_label" for="use-mancer-api-checkbox">
|
||||
<span data-i18n="Use API key (Only required for Mancer)">
|
||||
Click this box (and add your API key!):
|
||||
</span>
|
||||
<input id="use-mancer-api-checkbox" type="checkbox" />
|
||||
</label>
|
||||
</div>
|
||||
<div id="mancer-api-ui" style="display:none;">
|
||||
<h4 data-i18n="Mancer API key">Mancer API key</h4>
|
||||
<div class="flex-container">
|
||||
<input id="api_key_mancer" name="api_key_mancer" class="text_pole flex1 wide100p" maxlength="500" size="35" type="text" autocomplete="off">
|
||||
<div title="Clear your API key" data-i18n="[title]Clear your API key" class="menu_button fa-solid fa-circle-xmark clear-api-key" data-key="api_key_mancer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input id="api_button_textgenerationwebui" class="menu_button" type="submit" value="Connect">
|
||||
<div id="api_loading_textgenerationwebui" class="api-load-icon fa-solid fa-hourglass fa-spin"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<div data-for="api_key_mancer" class="neutral_warning" data-i18n="For privacy reasons, your API key will be hidden after you reload the page.">
|
||||
For privacy reasons, your API key will be hidden after you reload the page.
|
||||
</div>
|
||||
<div class="flex1">
|
||||
<h4 data-i18n="Blocking API url">Blocking API url</h4>
|
||||
<small>Example: http://127.0.0.1:5000/</small>
|
||||
<input id="textgenerationwebui_api_url_text" name="textgenerationwebui_api_url" class="text_pole wide100p" maxlength="500" value="" autocomplete="off">
|
||||
</div>
|
||||
<div class="flex1">
|
||||
<h4 data-i18n="Streaming API url">Streaming API url</h4>
|
||||
<small>Example: ws://127.0.0.1:5005/api/v1/stream</small>
|
||||
<input id="streaming_url_textgenerationwebui" type="text" class="text_pole wide100p" maxlength="500" value="" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<input id="api_button_textgenerationwebui" class="menu_button" type="submit" value="Connect">
|
||||
<div id="api_loading_textgenerationwebui" class="api-load-icon fa-solid fa-hourglass fa-spin"></div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="online_status4">
|
||||
<div class="online_status_indicator4"></div>
|
||||
<div class="online_status_text4" data-i18n="Not connected">Not connected</div>
|
||||
@ -2639,6 +2664,14 @@
|
||||
<option value="3" data-i18n="Bottom of Author's Note">Bottom of Author's Note</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="range-block">
|
||||
<label for="persona_show_notifications" class="checkbox_label">
|
||||
<input id="persona_show_notifications" type="checkbox" />
|
||||
<span data-i18n="Show Notifications Show notifications on switching personas">
|
||||
Show notifications on switching personas
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex1">
|
||||
<h4 class="title_restorable">
|
||||
@ -3276,7 +3309,7 @@
|
||||
<div title="Rename chat file" class="renameChatButton fa-solid fa-pen" data-i18n="[title]Rename chat file"></div>
|
||||
<div title="Export JSONL chat file" data-format="jsonl" class="exportRawChatButton fa-solid fa-file-export" data-i18n="[title]Export JSONL chat file"></div>
|
||||
<div title="Download chat as plain text document" data-format="txt" class="exportChatButton fa-solid fa-file-lines" data-i18n="[title]Download chat as plain text document"></div>
|
||||
<div title="Delete chat file" file_name="" class="PastChat_cross fa-solid fa-circle-xmark" data-i18n="[title]Delete chat file"></div>
|
||||
<div title="Delete chat file" file_name="" class="PastChat_cross fa-solid fa-skull" data-i18n="[title]Delete chat file"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
loadTextGenSettings,
|
||||
generateTextGenWithStreaming,
|
||||
getTextGenGenerationData,
|
||||
formatTextGenURL,
|
||||
} from "./scripts/textgen-settings.js";
|
||||
|
||||
import {
|
||||
@ -721,6 +722,7 @@ let is_get_status = false;
|
||||
let is_get_status_novel = false;
|
||||
let is_api_button_press = false;
|
||||
let is_api_button_press_novel = false;
|
||||
let api_use_mancer_webui = false;
|
||||
|
||||
let is_send_press = false; //Send generation
|
||||
let add_mes_without_animation = false;
|
||||
@ -854,9 +856,9 @@ async function getStatus() {
|
||||
type: "POST", //
|
||||
url: "/getstatus", //
|
||||
data: JSON.stringify({
|
||||
api_server:
|
||||
main_api == "kobold" ? api_server : api_server_textgenerationwebui,
|
||||
api_server: main_api == "kobold" ? api_server : api_server_textgenerationwebui,
|
||||
main_api: main_api,
|
||||
use_mancer: main_api == "textgenerationwebui" ? api_use_mancer_webui : false,
|
||||
}),
|
||||
beforeSend: function () { },
|
||||
cache: false,
|
||||
@ -883,6 +885,11 @@ async function getStatus() {
|
||||
kai_settings.can_use_streaming = canUseKoboldStreaming(data.koboldVersion);
|
||||
}
|
||||
|
||||
// We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
|
||||
if (online_status == "no_connection" && data.response) {
|
||||
toastr.error(data.response, "API Error", {timeOut: 5000, preventDuplicates:true})
|
||||
}
|
||||
|
||||
//console.log(online_status);
|
||||
resultCheckStatus();
|
||||
if (online_status !== "no_connection") {
|
||||
@ -2716,6 +2723,7 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
}
|
||||
else if (main_api == 'textgenerationwebui') {
|
||||
generate_data = getTextGenGenerationData(finalPromt, this_amount_gen, isImpersonate);
|
||||
generate_data.use_mancer = api_use_mancer_webui;
|
||||
}
|
||||
else if (main_api == 'novel') {
|
||||
const this_settings = novelai_settings[novelai_setting_names[nai_settings.preset_settings_novel]];
|
||||
@ -2978,6 +2986,13 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
||||
activateSendButtons();
|
||||
//console.log('runGenerate calling showSwipeBtns');
|
||||
showSwipeButtons();
|
||||
|
||||
if (main_api == 'textgenerationwebui' && api_use_mancer_webui) {
|
||||
const errorText = `<h3>Inferencer endpoint is unhappy!</h3>
|
||||
Returned status <tt>${data.status}</tt> with the reason:<br/>
|
||||
${data.response}`;
|
||||
callPopup(errorText, 'text');
|
||||
}
|
||||
}
|
||||
console.debug('/savechat called by /Generate');
|
||||
|
||||
@ -4554,7 +4569,9 @@ export function setUserName(value) {
|
||||
name1 = default_user_name;
|
||||
console.log(`User name changed to ${name1}`);
|
||||
$("#your_name").val(name1);
|
||||
toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated');
|
||||
if (power_user.persona_show_notifications) {
|
||||
toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated');
|
||||
}
|
||||
saveSettings("change_name");
|
||||
}
|
||||
|
||||
@ -4653,7 +4670,7 @@ function setUserAvatar() {
|
||||
const personaName = power_user.personas[user_avatar];
|
||||
if (personaName && name1 !== personaName) {
|
||||
const lockedPersona = chat_metadata['persona'];
|
||||
if (lockedPersona && lockedPersona !== user_avatar) {
|
||||
if (lockedPersona && lockedPersona !== user_avatar && power_user.persona_show_notifications) {
|
||||
toastr.info(
|
||||
`To permanently set "${personaName}" as the selected persona, unlock and relock it using the "Lock" button. Otherwise, the selection resets upon reloading the chat.`,
|
||||
`This chat is locked to a different persona (${power_user.personas[lockedPersona]}).`,
|
||||
@ -4760,7 +4777,9 @@ async function setDefaultPersona() {
|
||||
}
|
||||
|
||||
console.log(`Removing default persona ${avatarId}`);
|
||||
toastr.info('This persona will no longer be used by default when you open a new chat.', `Default persona removed`);
|
||||
if (power_user.persona_show_notifications) {
|
||||
toastr.info('This persona will no longer be used by default when you open a new chat.', `Default persona removed`);
|
||||
}
|
||||
delete power_user.default_persona;
|
||||
} else {
|
||||
const confirm = await callPopup(`<h3>Are you sure you want to set "${personaName}" as the default persona?</h3>
|
||||
@ -4772,7 +4791,9 @@ async function setDefaultPersona() {
|
||||
}
|
||||
|
||||
power_user.default_persona = avatarId;
|
||||
toastr.success('This persona will be used by default when you open a new chat.', `Default persona set to ${personaName}`);
|
||||
if (power_user.persona_show_notifications) {
|
||||
toastr.success('This persona will be used by default when you open a new chat.', `Default persona set to ${personaName}`);
|
||||
}
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
@ -4835,18 +4856,22 @@ function lockUserNameToChat() {
|
||||
console.log(`Unlocking persona for this chat ${chat_metadata['persona']}`);
|
||||
delete chat_metadata['persona'];
|
||||
saveMetadata();
|
||||
toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked');
|
||||
if (power_user.persona_show_notifications) {
|
||||
toastr.info('User persona is now unlocked for this chat. Click the "Lock" again to revert.', 'Persona unlocked');
|
||||
}
|
||||
updateUserLockIcon();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(user_avatar in power_user.personas)) {
|
||||
console.log(`Creating a new persona ${user_avatar}`);
|
||||
toastr.info(
|
||||
'Creating a new persona for currently selected user name and avatar...',
|
||||
'Persona not set for this avatar',
|
||||
{ timeOut: 10000, extendedTimeOut: 20000, },
|
||||
);
|
||||
if (power_user.persona_show_notifications) {
|
||||
toastr.info(
|
||||
'Creating a new persona for currently selected user name and avatar...',
|
||||
'Persona not set for this avatar',
|
||||
{ timeOut: 10000, extendedTimeOut: 20000, },
|
||||
);
|
||||
}
|
||||
power_user.personas[user_avatar] = name1;
|
||||
power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.BEFORE_CHAR };
|
||||
}
|
||||
@ -4855,7 +4880,9 @@ function lockUserNameToChat() {
|
||||
saveMetadata();
|
||||
saveSettingsDebounced();
|
||||
console.log(`Locking persona for this chat ${user_avatar}`);
|
||||
toastr.success(`User persona is locked to ${name1} in this chat`);
|
||||
if (power_user.persona_show_notifications) {
|
||||
toastr.success(`User persona is locked to ${name1} in this chat`);
|
||||
}
|
||||
updateUserLockIcon();
|
||||
}
|
||||
|
||||
@ -5076,11 +5103,13 @@ async function getSettings(type) {
|
||||
|
||||
setWorldInfoSettings(settings, data);
|
||||
|
||||
api_server_textgenerationwebui =
|
||||
settings.api_server_textgenerationwebui;
|
||||
api_server_textgenerationwebui = settings.api_server_textgenerationwebui;
|
||||
$("#textgenerationwebui_api_url_text").val(
|
||||
api_server_textgenerationwebui
|
||||
);
|
||||
api_use_mancer_webui = settings.api_use_mancer_webui
|
||||
$('#use-mancer-api-checkbox').prop("checked", api_use_mancer_webui);
|
||||
$('#use-mancer-api-checkbox').trigger("change");
|
||||
|
||||
selected_button = settings.selected_button;
|
||||
|
||||
@ -5114,6 +5143,7 @@ async function saveSettings(type) {
|
||||
active_group: active_group,
|
||||
api_server: api_server,
|
||||
api_server_textgenerationwebui: api_server_textgenerationwebui,
|
||||
api_use_mancer_webui: api_use_mancer_webui,
|
||||
preset_settings: preset_settings,
|
||||
user_avatar: user_avatar,
|
||||
amount_gen: amount_gen,
|
||||
@ -7508,7 +7538,7 @@ $(document).ready(function () {
|
||||
<h3>Delete the character?</h3>
|
||||
<b>THIS IS PERMANENT!<br><br>
|
||||
<label for="del_char_checkbox" class="checkbox_label justifyCenter">
|
||||
<input type="checkbox" id="del_char_checkbox" checked />
|
||||
<input type="checkbox" id="del_char_checkbox" />
|
||||
<span>Also delete the chat files</span>
|
||||
</label><br></b>`
|
||||
);
|
||||
@ -7691,16 +7721,28 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
$("#api_button_textgenerationwebui").click(function (e) {
|
||||
$("#use-mancer-api-checkbox").on("change", function (e) {
|
||||
const enabled = $("#use-mancer-api-checkbox").prop("checked");
|
||||
$("#mancer-api-ui").toggle(enabled);
|
||||
api_use_mancer_webui = enabled;
|
||||
saveSettingsDebounced();
|
||||
getStatus();
|
||||
});
|
||||
|
||||
$("#api_button_textgenerationwebui").click(async function (e) {
|
||||
e.stopPropagation();
|
||||
if ($("#textgenerationwebui_api_url_text").val() != "") {
|
||||
let value = formatKoboldUrl($("#textgenerationwebui_api_url_text").val().trim());
|
||||
|
||||
let value = formatTextGenURL($("#textgenerationwebui_api_url_text").val().trim())
|
||||
if (!value) {
|
||||
callPopup('Please enter a valid URL.', 'text');
|
||||
callPopup('Please enter a valid URL.<br/>WebUI URLs should end with <tt>/api</tt>', 'text');
|
||||
return;
|
||||
}
|
||||
|
||||
const mancer_key = $("#api_key_mancer").val().trim();
|
||||
if (mancer_key.length) {
|
||||
await writeSecret(SECRET_KEYS.MANCER, mancer_key);
|
||||
}
|
||||
|
||||
$("#textgenerationwebui_api_url_text").val(value);
|
||||
$("#api_loading_textgenerationwebui").css("display", "inline-block");
|
||||
$("#api_button_textgenerationwebui").css("display", "none");
|
||||
|
@ -534,12 +534,14 @@ export function dragElement(elmnt) {
|
||||
|
||||
if (elmntHeader.length) {
|
||||
elmntHeader.off('mousedown').on('mousedown', (e) => {
|
||||
|
||||
hasBeenDraggedByUser = true
|
||||
observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
|
||||
dragMouseDown(e);
|
||||
});
|
||||
$(elmnt).off('mousedown').on('mousedown', () => { isMouseDown = true })
|
||||
} else {
|
||||
elmnt.off('mousedown').on('mousedown', dragMouseDown);
|
||||
$(elmnt).off('mousedown').on('mousedown', () => {
|
||||
isMouseDown = true
|
||||
observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
|
||||
})
|
||||
}
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
@ -618,7 +620,7 @@ export function dragElement(elmnt) {
|
||||
}
|
||||
|
||||
//prevent resizing from top left into the top bar
|
||||
if (top <= 40 && maxX >= topBarFirstX && left <= topBarFirstX
|
||||
if (top < 40 && maxX >= topBarFirstX && left <= topBarFirstX
|
||||
) {
|
||||
console.debug('prevent topbar underlap resize')
|
||||
elmnt.css('width', width - 1 + "px");
|
||||
@ -674,8 +676,6 @@ export function dragElement(elmnt) {
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });
|
||||
|
||||
function dragMouseDown(e) {
|
||||
|
||||
if (e) {
|
||||
@ -745,6 +745,7 @@ export function dragElement(elmnt) {
|
||||
$("body").css("overflow", "");
|
||||
// Clear the "data-dragged" attribute
|
||||
elmnt.attr('data-dragged', 'false');
|
||||
observer.disconnect()
|
||||
console.debug(`Saving ${elmntName} UI position`)
|
||||
saveSettingsDebounced();
|
||||
|
||||
|
108
public/scripts/extensions/bulk-edit/index.js
Normal file
108
public/scripts/extensions/bulk-edit/index.js
Normal file
@ -0,0 +1,108 @@
|
||||
import { characters, getCharacters, saveSettingsDebounced, handleDeleteCharacter } from "../../../script.js";
|
||||
|
||||
let is_bulk_edit = false;
|
||||
|
||||
/**
|
||||
* Toggles bulk edit mode on/off when the edit button is clicked.
|
||||
*/
|
||||
function onEditButtonClick() {
|
||||
console.log("Edit button clicked");
|
||||
// toggle bulk edit mode
|
||||
if (is_bulk_edit) {
|
||||
disableBulkSelect();
|
||||
// hide the delete button
|
||||
$("#bulkDeleteButton").hide();
|
||||
is_bulk_edit = false;
|
||||
} else {
|
||||
enableBulkSelect();
|
||||
// show the delete button
|
||||
$("#bulkDeleteButton").show();
|
||||
is_bulk_edit = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the character with the given chid.
|
||||
*
|
||||
* @param {string} this_chid - The chid of the character to delete.
|
||||
*/
|
||||
async function deleteCharacter(this_chid) {
|
||||
await handleDeleteCharacter("del_ch", this_chid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all characters that have been selected via the bulk checkboxes.
|
||||
*/
|
||||
async function onDeleteButtonClick() {
|
||||
console.log("Delete button clicked");
|
||||
|
||||
// Create a mapping of chid to avatar
|
||||
let toDelete = [];
|
||||
$(".bulk_select_checkbox:checked").each((i, el) => {
|
||||
const chid = $(el).parent().attr("chid");
|
||||
const avatar = characters[chid].avatar;
|
||||
// Add the avatar to the list of avatars to delete
|
||||
toDelete.push(avatar);
|
||||
});
|
||||
|
||||
// Delete the characters
|
||||
for (const avatar of toDelete) {
|
||||
console.log(`Deleting character with avatar ${avatar}`);
|
||||
await getCharacters();
|
||||
|
||||
//chid should be the key of the character with the given avatar
|
||||
const chid = Object.keys(characters).find((key) => characters[key].avatar === avatar);
|
||||
console.log(`Deleting character with chid ${chid}`);
|
||||
await deleteCharacter(chid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the bulk edit and delete buttons to the UI.
|
||||
*/
|
||||
function addButtons() {
|
||||
const editButton = $(
|
||||
"<i id='bulkEditButton' class='fa-solid fa-edit menu_button bulkEditButton' title='Bulk edit characters'></i>"
|
||||
);
|
||||
const deleteButton = $(
|
||||
"<i id='bulkDeleteButton' class='fa-solid fa-trash menu_button bulkDeleteButton' title='Bulk delete characters' style='display: none;'></i>"
|
||||
);
|
||||
|
||||
$("#charListGridToggle").after(editButton, deleteButton);
|
||||
|
||||
$("#bulkEditButton").on("click", onEditButtonClick);
|
||||
$("#bulkDeleteButton").on("click", onDeleteButtonClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables bulk selection by adding a checkbox next to each character.
|
||||
*/
|
||||
function enableBulkSelect() {
|
||||
$(".character_select").each((i, el) => {
|
||||
const character = $(el).text();
|
||||
const checkbox = $("<input type='checkbox' class='bulk_select_checkbox'>");
|
||||
checkbox.on("change", () => {
|
||||
// Do something when the checkbox is changed
|
||||
});
|
||||
$(el).prepend(checkbox);
|
||||
});
|
||||
// We also need to disable the default click event for the character_select divs
|
||||
$(document).on("click", ".bulk_select_checkbox", function (event) {
|
||||
event.stopImmediatePropagation();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables bulk selection by removing the checkboxes.
|
||||
*/
|
||||
function disableBulkSelect() {
|
||||
$(".bulk_select_checkbox").remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point that runs on page load.
|
||||
*/
|
||||
jQuery(async () => {
|
||||
addButtons();
|
||||
// loadSettings();
|
||||
});
|
11
public/scripts/extensions/bulk-edit/manifest.json
Normal file
11
public/scripts/extensions/bulk-edit/manifest.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"display_name": "Bulk Card Editor",
|
||||
"loading_order": 9,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "city-unit",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/city-unit"
|
||||
}
|
@ -449,7 +449,7 @@ async function loadLiveChar() {
|
||||
|
||||
function handleImageChange() {
|
||||
const imgElement = document.querySelector('img#expression-image.expression');
|
||||
|
||||
|
||||
if (!imgElement) {
|
||||
console.log("Cannot find addExpressionImage()");
|
||||
return;
|
||||
@ -459,6 +459,7 @@ function handleImageChange() {
|
||||
previousSrc = imgElement.src;
|
||||
// Method get IP of endpoint
|
||||
const live2dResultFeedSrc = `${getApiUrl()}/api/live2d/result_feed`;
|
||||
$('#expression-holder').css({ display: '' });
|
||||
if (imgElement.src !== live2dResultFeedSrc) {
|
||||
const expressionImageElement = document.querySelector('.expression_list_image');
|
||||
|
||||
@ -922,7 +923,7 @@ async function setExpression(character, expression, force) {
|
||||
if (imgElement) {
|
||||
console.log("setting value");
|
||||
imgElement.src = getApiUrl() + '/api/live2d/result_feed';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { getStringHash, debounce, waitUntilCondition, extractAllWords } from "../../utils.js";
|
||||
import { getContext, getApiUrl, extension_settings, doExtrasFetch, modules } from "../../extensions.js";
|
||||
import { eventSource, event_types, extension_prompt_types, generateQuietPrompt, is_send_press, saveSettingsDebounced, substituteParams } from "../../../script.js";
|
||||
import { is_group_generating, selected_group } from "../../group-chats.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = '1_memory';
|
||||
@ -333,6 +334,10 @@ async function summarizeChat(context) {
|
||||
|
||||
async function summarizeChatMain(context, force) {
|
||||
try {
|
||||
// Wait for group to finish generating
|
||||
if (selected_group) {
|
||||
await waitUntilCondition(() => is_group_generating === false, 1000, 10);
|
||||
}
|
||||
// Wait for the send button to be released
|
||||
waitUntilCondition(() => is_send_press === false, 30000, 100);
|
||||
} catch {
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
|
||||
import { registerSlashCommand } from "./slash-commands.js";
|
||||
|
||||
import { delay, debounce } from "./utils.js";
|
||||
import { delay } from "./utils.js";
|
||||
|
||||
export {
|
||||
loadPowerUserSettings,
|
||||
@ -184,6 +184,7 @@ let power_user = {
|
||||
|
||||
persona_description: '',
|
||||
persona_description_position: persona_description_positions.BEFORE_CHAR,
|
||||
persona_show_notifications: true,
|
||||
|
||||
custom_stopping_strings: '',
|
||||
fuzzy_search: false,
|
||||
@ -678,6 +679,7 @@ function loadPowerUserSettings(settings, data) {
|
||||
$('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold);
|
||||
$('#custom_stopping_strings').val(power_user.custom_stopping_strings);
|
||||
$('#fuzzy_search_checkbox').prop("checked", power_user.fuzzy_search);
|
||||
$('#persona_show_notifications').prop("checked", power_user.persona_show_notifications);
|
||||
|
||||
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
|
||||
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
|
||||
@ -1998,6 +2000,11 @@ $(document).ready(() => {
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#persona_show_notifications').on('input', function () {
|
||||
power_user.persona_show_notifications = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$(window).on('focus', function () {
|
||||
browser_has_focus = true;
|
||||
});
|
||||
|
@ -2,6 +2,7 @@ import { callPopup, getRequestHeaders } from "../script.js";
|
||||
|
||||
export const SECRET_KEYS = {
|
||||
HORDE: 'api_key_horde',
|
||||
MANCER: 'api_key_mancer',
|
||||
OPENAI: 'api_key_openai',
|
||||
NOVEL: 'api_key_novel',
|
||||
CLAUDE: 'api_key_claude',
|
||||
@ -11,6 +12,7 @@ export const SECRET_KEYS = {
|
||||
|
||||
const INPUT_MAP = {
|
||||
[SECRET_KEYS.HORDE]: '#horde_api_key',
|
||||
[SECRET_KEYS.MANCER]: '#api_key_mancer',
|
||||
[SECRET_KEYS.OPENAI]: '#api_key_openai',
|
||||
[SECRET_KEYS.NOVEL]: '#api_key_novel',
|
||||
[SECRET_KEYS.CLAUDE]: '#api_key_claude',
|
||||
|
@ -10,6 +10,7 @@ export {
|
||||
textgenerationwebui_settings,
|
||||
loadTextGenSettings,
|
||||
generateTextGenWithStreaming,
|
||||
formatTextGenURL,
|
||||
}
|
||||
|
||||
const textgenerationwebui_settings = {
|
||||
@ -94,6 +95,16 @@ function selectPreset(name) {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function formatTextGenURL(value) {
|
||||
try {
|
||||
const url = new URL(value);
|
||||
if (url.pathname.endsWith('/api')) {
|
||||
return url.toString();
|
||||
}
|
||||
} catch { } // Try and Catch both fall through to the same return.
|
||||
return null;
|
||||
}
|
||||
|
||||
function convertPresets(presets) {
|
||||
return Array.isArray(presets) ? presets.map(JSON.parse) : [];
|
||||
}
|
||||
|
@ -3040,9 +3040,22 @@ h5 {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
|
||||
.PastChat_cross:hover {
|
||||
color: red;
|
||||
filter: drop-shadow(0 0 2px red);
|
||||
-webkit-animation: infinite-spinning 1s ease-out 0s infinite normal;
|
||||
animation: infinite-spinning 1s ease-out 0s infinite normal;
|
||||
}
|
||||
|
||||
/* HEINOUS */
|
||||
@keyframes infinite-spinning {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
#export_character_div {
|
||||
@ -4409,6 +4422,10 @@ toolcool-color-picker {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.indent20p {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.wi-enter-footer-text {
|
||||
font-size: calc(var(--mainFontSize) * 0.8);
|
||||
color: var(--SmartThemeBodyColor);
|
||||
|
35
server.js
35
server.js
@ -146,6 +146,14 @@ let response_getstatus;
|
||||
let first_run = true;
|
||||
|
||||
|
||||
|
||||
function get_mancer_headers() {
|
||||
const api_key_mancer = readSecret(SECRET_KEYS.MANCER);
|
||||
return api_key_mancer ? { "X-API-KEY": api_key_mancer} : {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
//RossAscends: Added function to format dates used in files and chat timestamps to a humanized format.
|
||||
//Mostly I wanted this to be for file names, but couldn't figure out exactly where the filename save code was as everything seemed to be connected.
|
||||
//During testing, this performs the same as previous date.now() structure.
|
||||
@ -640,13 +648,22 @@ app.post("/generate_textgenerationwebui", jsonParser, async function (request, r
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
if (request.body.use_mancer) {
|
||||
args.headers = Object.assign(args.headers, get_mancer_headers());
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await postAsync(api_server + "/v1/generate", args);
|
||||
console.log(data);
|
||||
return response_generate.send(data);
|
||||
} catch (error) {
|
||||
retval = { error: true, status: error.status, response: error.statusText };
|
||||
console.log(error);
|
||||
return response_generate.send({ error: true });
|
||||
try {
|
||||
retval.response = await error.json();
|
||||
retval.response = retval.response.result;
|
||||
} catch {}
|
||||
return response_generate.send(retval);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -710,6 +727,11 @@ app.post("/getstatus", jsonParser, async function (request, response_getstatus =
|
||||
var args = {
|
||||
headers: { "Content-Type": "application/json" }
|
||||
};
|
||||
|
||||
if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
|
||||
args.headers = Object.assign(args.headers, get_mancer_headers());
|
||||
}
|
||||
|
||||
var url = api_server + "/v1/model";
|
||||
let version = '';
|
||||
let koboldVersion = {};
|
||||
@ -730,18 +752,18 @@ app.post("/getstatus", jsonParser, async function (request, response_getstatus =
|
||||
};
|
||||
}
|
||||
}
|
||||
client.get(url, args, function (data, response) {
|
||||
client.get(url, args, async function (data, response) {
|
||||
if (typeof data !== 'object') {
|
||||
data = {};
|
||||
}
|
||||
if (response.statusCode == 200) {
|
||||
data.version = version;
|
||||
data.koboldVersion = koboldVersion;
|
||||
if (data.result != "ReadOnly") {
|
||||
} else {
|
||||
if (data.result == "ReadOnly") {
|
||||
data.result = "no_connection";
|
||||
}
|
||||
} else {
|
||||
data.response = data.result;
|
||||
data.result = "no_connection";
|
||||
}
|
||||
response_getstatus.send(data);
|
||||
@ -3453,6 +3475,10 @@ app.post("/tokenize_via_api", jsonParser, async function (request, response) {
|
||||
body: JSON.stringify({ "prompt": text }),
|
||||
headers: { "Content-Type": "application/json" }
|
||||
};
|
||||
|
||||
if (main_api == 'textgenerationwebui' && request.body.use_mancer) {
|
||||
args.headers = Object.assign(args.headers, get_mancer_headers());
|
||||
}
|
||||
|
||||
const data = await postAsync(api_server + "/v1/token-count", args);
|
||||
console.log(data);
|
||||
@ -3659,6 +3685,7 @@ const SECRETS_FILE = './secrets.json';
|
||||
const SETTINGS_FILE = './public/settings.json';
|
||||
const SECRET_KEYS = {
|
||||
HORDE: 'api_key_horde',
|
||||
MANCER: 'api_key_mancer',
|
||||
OPENAI: 'api_key_openai',
|
||||
NOVEL: 'api_key_novel',
|
||||
CLAUDE: 'api_key_claude',
|
||||
|
Loading…
Reference in New Issue
Block a user