SillyTavern/public/scripts/power-user.js

2145 lines
76 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
saveSettingsDebounced,
scrollChatToBottom,
characters,
callPopup,
getStatus,
reloadMarkdownProcessor,
reloadCurrentChat,
getRequestHeaders,
substituteParams,
eventSource,
event_types,
getCurrentChatId,
printCharacters,
name1,
name2,
setCharacterId,
setEditedMessageId
} from "../script.js";
import { isMobile, initMovingUI } from "./RossAscends-mods.js";
import {
groups,
resetSelectedGroup,
selected_group,
} from "./group-chats.js";
import { registerSlashCommand } from "./slash-commands.js";
import { delay } from "./utils.js";
export {
loadPowerUserSettings,
loadMovingUIState,
collapseNewlines,
playMessageSound,
sortEntitiesList,
fixMarkdown,
power_user,
pygmalion_options,
tokenizers,
send_on_enter_options,
};
export const MAX_CONTEXT_DEFAULT = 4096;
const MAX_CONTEXT_UNLOCKED = 65536;
const defaultStoryString = `{{#if description}}{{description}}{{/if}}
{{#if personality}}{{personality}}{{/if}}
{{#if scenario}}Scenario: {{scenario}}{{/if}}`;
const defaultExampleSeparator = '***';
const defaultChatStart = '***';
const avatar_styles = {
ROUND: 0,
RECTANGULAR: 1,
}
export const chat_styles = {
DEFAULT: 0,
BUBBLES: 1,
DOCUMENT: 2,
}
const pygmalion_options = {
DISABLED: -1,
AUTO: 0,
ENABLED: 1,
}
const tokenizers = {
NONE: 0,
GPT3: 1,
CLASSIC: 2,
LLAMA: 3,
NERD: 4,
NERD2: 5,
API: 6,
BEST_MATCH: 99,
}
const send_on_enter_options = {
DISABLED: -1,
AUTO: 0,
ENABLED: 1,
}
export const persona_description_positions = {
BEFORE_CHAR: 0,
AFTER_CHAR: 1,
TOP_AN: 2,
BOTTOM_AN: 3,
}
let power_user = {
tokenizer: tokenizers.BEST_MATCH,
token_padding: 64,
collapse_newlines: false,
pygmalion_formatting: pygmalion_options.AUTO,
pin_examples: false,
trim_sentences: false,
include_newline: false,
always_force_name2: false,
user_prompt_bias: '',
show_user_prompt_bias: true,
multigen: false,
multigen_first_chunk: 50,
multigen_next_chunks: 30,
markdown_escape_strings: '',
fast_ui_mode: true,
avatar_style: avatar_styles.ROUND,
chat_display: chat_styles.DEFAULT,
chat_width: 50,
never_resize_avatars: false,
show_card_avatar_urls: false,
play_message_sound: false,
play_sound_unfocused: true,
auto_save_msg_edits: false,
confirm_message_delete: true,
sort_field: 'name',
sort_order: 'asc',
sort_rule: null,
font_scale: 1,
blur_strength: 10,
shadow_width: 2,
main_text_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeBodyColor').trim()}`,
italics_text_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeEmColor').trim()}`,
quote_text_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeQuoteColor').trim()}`,
blur_tint_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeBlurTintColor').trim()}`,
user_mes_blur_tint_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeUserMesBlurTintColor').trim()}`,
bot_mes_blur_tint_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeBotMesBlurTintColor').trim()}`,
shadow_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeShadowColor').trim()}`,
waifuMode: false,
movingUI: false,
movingUIState: {},
movingUIPreset: '',
noShadows: false,
theme: 'Default (Dark) 1.7.1',
auto_swipe: false,
auto_swipe_minimum_length: 0,
auto_swipe_blacklist: [],
auto_swipe_blacklist_threshold: 2,
auto_scroll_chat_to_bottom: true,
auto_fix_generated_markdown: true,
send_on_enter: send_on_enter_options.AUTO,
console_log_prompts: false,
render_formulas: false,
allow_name1_display: false,
allow_name2_display: false,
//removeXML: false,
hotswap_enabled: true,
timer_enabled: true,
timestamps_enabled: true,
timestamp_model_icon: false,
mesIDDisplay_enabled: false,
max_context_unlocked: false,
prefer_character_prompt: true,
prefer_character_jailbreak: true,
continue_on_send: false,
trim_spaces: true,
relaxed_api_urls: false,
instruct: {
enabled: false,
wrap: true,
names: false,
system_prompt: "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\nWrite {{char}}'s next reply in a fictional roleplay chat between {{user}} and {{char}}. Write 1 reply only.",
system_sequence: '',
stop_sequence: '',
input_sequence: '### Instruction:',
output_sequence: '### Response:',
last_output_sequence: '',
preset: 'Alpaca',
separator_sequence: '',
macro: false,
names_force_groups: true,
},
context: {
preset: 'Default',
story_string: defaultStoryString,
chat_start: defaultChatStart,
example_separator: defaultExampleSeparator,
},
personas: {},
default_persona: null,
persona_descriptions: {},
persona_description: '',
persona_description_position: persona_description_positions.BEFORE_CHAR,
persona_show_notifications: true,
custom_stopping_strings: '',
custom_stopping_strings_macro: true,
fuzzy_search: false,
encode_tags: false,
lazy_load: 0,
};
let themes = [];
let movingUIPresets = [];
let instruct_presets = [];
let context_presets = [];
const storage_keys = {
ui_language: "language",
fast_ui_mode: "TavernAI_fast_ui_mode",
avatar_style: "TavernAI_avatar_style",
chat_display: "TavernAI_chat_display",
chat_width: "chat_width",
font_scale: "TavernAI_font_scale",
main_text_color: "TavernAI_main_text_color",
italics_text_color: "TavernAI_italics_text_color",
quote_text_color: "TavernAI_quote_text_color",
blur_tint_color: "TavernAI_blur_tint_color",
user_mes_blur_tint_color: "TavernAI_user_mes_blur_tint_color",
bot_mes_blur_tint_color: "TavernAI_bot_mes_blur_tint_color",
blur_strength: "TavernAI_blur_strength",
shadow_color: "TavernAI_shadow_color",
shadow_width: "TavernAI_shadow_width",
waifuMode: "TavernAI_waifuMode",
movingUI: "TavernAI_movingUI",
noShadows: "TavernAI_noShadows",
hotswap_enabled: 'HotswapEnabled',
timer_enabled: 'TimerEnabled',
timestamps_enabled: 'TimestampsEnabled',
timestamp_model_icon: 'TimestampModelIcon',
mesIDDisplay_enabled: 'mesIDDisplayEnabled',
};
let browser_has_focus = true;
function playMessageSound() {
if (!power_user.play_message_sound) {
return;
}
if (power_user.play_sound_unfocused && browser_has_focus) {
return;
}
const audio = document.getElementById('audio_message_sound');
audio.volume = 0.8;
audio.pause();
audio.currentTime = 0;
audio.play();
}
function collapseNewlines(x) {
return x.replaceAll(/\n+/g, "\n");
}
function fixMarkdown(text) {
// fix formatting problems in markdown
// e.g.:
// "^example * text*\n" -> "^example *text*\n"
// "^*example * text\n" -> "^*example* text\n"
// "^example *text *\n" -> "^example *text*\n"
// "^* example * text\n" -> "^*example* text\n"
// take note that the side you move the asterisk depends on where its pairing is
// i.e. both of the following strings have the same broken asterisk ' * ',
// but you move the first to the left and the second to the right, to match the non-broken asterisk "^example * text*\n" "^*example * text\n"
// and you HAVE to handle the cases where multiple pairs of asterisks exist in the same line
// i.e. "^example * text* * harder problem *\n" -> "^example *text* *harder problem*\n"
// Find pairs of formatting characters and capture the text in between them
const format = /([\*_]{1,2})([\s\S]*?)\1/gm;
let matches = [];
let match;
while ((match = format.exec(text)) !== null) {
matches.push(match);
}
// Iterate through the matches and replace adjacent spaces immediately beside formatting characters
let newText = text;
for (let i = matches.length - 1; i >= 0; i--) {
let matchText = matches[i][0];
let replacementText = matchText.replace(/(\*|_)([\t \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]+)|([\t \u00a0\u1680\u2000-\u200a\u202f\u205f\u3000\ufeff]+)(\*|_)/g, '$1$4');
newText = newText.slice(0, matches[i].index) + replacementText + newText.slice(matches[i].index + matchText.length);
}
return newText;
}
function switchHotswap() {
const value = localStorage.getItem(storage_keys.hotswap_enabled);
power_user.hotswap_enabled = value === null ? true : value == "true";
$("body").toggleClass("no-hotswap", !power_user.hotswap_enabled);
$("#hotswapEnabled").prop("checked", power_user.hotswap_enabled);
}
function switchTimer() {
const value = localStorage.getItem(storage_keys.timer_enabled);
power_user.timer_enabled = value === null ? true : value == "true";
$("body").toggleClass("no-timer", !power_user.timer_enabled);
$("#messageTimerEnabled").prop("checked", power_user.timer_enabled);
}
function switchTimestamps() {
const value = localStorage.getItem(storage_keys.timestamps_enabled);
power_user.timestamps_enabled = value === null ? true : value == "true";
$("body").toggleClass("no-timestamps", !power_user.timestamps_enabled);
$("#messageTimestampsEnabled").prop("checked", power_user.timestamps_enabled);
}
function switchIcons() {
const value = localStorage.getItem(storage_keys.timestamp_model_icon);
power_user.timestamp_model_icon = value === null ? true : value == "true";
$("body").toggleClass("no-modelIcons", !power_user.timestamp_model_icon);
$("#messageModelIconEnabled").prop("checked", power_user.timestamp_model_icon);
}
function switchMesIDDisplay() {
const value = localStorage.getItem(storage_keys.mesIDDisplay_enabled);
power_user.mesIDDisplay_enabled = value === null ? true : value == "true";
$("body").toggleClass("no-mesIDDisplay", !power_user.mesIDDisplay_enabled);
$("#MesIDDisplayEnabled").prop("checked", power_user.mesIDDisplay_enabled);
}
function switchUiMode() {
const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
power_user.fast_ui_mode = fastUi === null ? true : fastUi == "true";
$("body").toggleClass("no-blur", power_user.fast_ui_mode);
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
}
function toggleWaifu() {
$("#waifuMode").trigger("click");
}
function switchWaifuMode() {
$("body").toggleClass("waifuMode", power_user.waifuMode);
$("#waifuMode").prop("checked", power_user.waifuMode);
scrollChatToBottom();
}
function switchSpoilerMode() {
if (power_user.spoiler_free_mode) {
$("#description_div").hide();
$("#description_textarea").hide();
$("#firstmessage_textarea").hide();
$("#first_message_div").hide();
$("#spoiler_free_desc").show();
}
else {
$("#description_div").show();
$("#description_textarea").show();
$("#firstmessage_textarea").show();
$("#first_message_div").show();
$("#spoiler_free_desc").hide();
}
}
function peekSpoilerMode() {
$("#description_div").toggle();
$("#description_textarea").toggle();
$("#firstmessage_textarea").toggle();
$("#first_message_div").toggle();
}
function switchMovingUI() {
const movingUI = localStorage.getItem(storage_keys.movingUI);
power_user.movingUI = movingUI === null ? false : movingUI == "true";
$("body").toggleClass("movingUI", power_user.movingUI);
if (power_user.movingUI === true) {
initMovingUI()
if (power_user.movingUIState) {
loadMovingUIState();
}
};
}
function noShadows() {
const noShadows = localStorage.getItem(storage_keys.noShadows);
power_user.noShadows = noShadows === null ? false : noShadows == "true";
$("body").toggleClass("noShadows", power_user.noShadows);
$("#noShadowsmode").prop("checked", power_user.noShadows);
scrollChatToBottom();
}
function applyAvatarStyle() {
power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
$("body").toggleClass("big-avatars", power_user.avatar_style === avatar_styles.RECTANGULAR);
$(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop("checked", true);
}
function applyChatDisplay() {
if (!power_user.chat_display === (null || undefined)) {
console.debug('applyChatDisplay: saw no chat display type defined')
return
}
console.debug(`applyChatDisplay: applying ${power_user.chat_display}`)
$(`#chat_display option[value=${power_user.chat_display}]`).attr("selected", true)
switch (power_user.chat_display) {
case 0: {
console.log('applying default chat')
$("body").removeClass("bubblechat");
$("body").removeClass("documentstyle");
break
}
case 1: {
console.log('applying bubblechat')
$("body").addClass("bubblechat");
$("body").removeClass("documentstyle");
break
}
case 2: {
console.log('applying document style')
$("body").removeClass("bubblechat");
$("body").addClass("documentstyle");
break
}
}
}
function applyChatWidth() {
power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
let r = document.documentElement;
r.style.setProperty('--sheldWidth', `${power_user.chat_width}vw`);
$('#chat_width_slider').val(power_user.chat_width);
}
async function applyThemeColor(type) {
if (type === 'main') {
document.documentElement.style.setProperty('--SmartThemeBodyColor', power_user.main_text_color);
}
if (type === 'italics') {
document.documentElement.style.setProperty('--SmartThemeEmColor', power_user.italics_text_color);
}
if (type === 'quote') {
document.documentElement.style.setProperty('--SmartThemeQuoteColor', power_user.quote_text_color);
}
/* if (type === 'fastUIBG') {
document.documentElement.style.setProperty('--SmartThemeFastUIBGColor', power_user.fastui_bg_color);
} */
if (type === 'blurTint') {
document.documentElement.style.setProperty('--SmartThemeBlurTintColor', power_user.blur_tint_color);
}
if (type === 'userMesBlurTint') {
document.documentElement.style.setProperty('--SmartThemeUserMesBlurTintColor', power_user.user_mes_blur_tint_color);
}
if (type === 'botMesBlurTint') {
document.documentElement.style.setProperty('--SmartThemeBotMesBlurTintColor', power_user.bot_mes_blur_tint_color);
}
if (type === 'shadow') {
document.documentElement.style.setProperty('--SmartThemeShadowColor', power_user.shadow_color);
}
}
async function applyBlurStrength() {
power_user.blur_strength = Number(localStorage.getItem(storage_keys.blur_strength) ?? 1);
document.documentElement.style.setProperty('--blurStrength', power_user.blur_strength);
$("#blur_strength_counter").text(power_user.blur_strength);
$("#blur_strength").val(power_user.blur_strength);
}
async function applyShadowWidth() {
power_user.shadow_width = Number(localStorage.getItem(storage_keys.shadow_width) ?? 2);
document.documentElement.style.setProperty('--shadowWidth', power_user.shadow_width);
$("#shadow_width_counter").text(power_user.shadow_width);
$("#shadow_width").val(power_user.shadow_width);
}
async function applyFontScale() {
power_user.font_scale = Number(localStorage.getItem(storage_keys.font_scale) ?? 1);
document.documentElement.style.setProperty('--fontScale', power_user.font_scale);
$("#font_scale_counter").text(power_user.font_scale);
$("#font_scale").val(power_user.font_scale);
}
async function applyTheme(name) {
const theme = themes.find(x => x.name == name);
if (!theme) {
return;
}
const themeProperties = [
{ key: 'main_text_color', selector: '#main-text-color-picker', type: 'main' },
{ key: 'italics_text_color', selector: '#italics-color-picker', type: 'italics' },
{ key: 'quote_text_color', selector: '#quote-color-picker', type: 'quote' },
{ key: 'blur_tint_color', selector: '#blur-tint-color-picker', type: 'blurTint' },
{ key: 'user_mes_blur_tint_color', selector: '#user-mes-blur-tint-color-picker', type: 'userMesBlurTint' },
{ key: 'bot_mes_blur_tint_color', selector: '#bot-mes-blur-tint-color-picker', type: 'botMesBlurTint' },
{ key: 'shadow_color', selector: '#shadow-color-picker', type: 'shadow' },
{
key: 'blur_strength',
action: async () => {
localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength);
await applyBlurStrength();
}
},
{
key: 'shadow_width',
action: async () => {
localStorage.setItem(storage_keys.shadow_width, power_user.shadow_width);
await applyShadowWidth();
}
},
{
key: 'font_scale',
action: async () => {
localStorage.setItem(storage_keys.font_scale, power_user.font_scale);
await applyFontScale();
}
},
{
key: 'fast_ui_mode',
action: async () => {
localStorage.setItem(storage_keys.fast_ui_mode, power_user.fast_ui_mode);
switchUiMode();
}
},
{
key: 'waifuMode',
action: async () => {
localStorage.setItem(storage_keys.waifuMode, power_user.waifuMode);
switchWaifuMode();
}
},
{
key: 'chat_display',
action: async () => {
localStorage.setItem(storage_keys.chat_display, power_user.chat_display);
applyChatDisplay();
}
},
{
key: 'avatar_style',
action: async () => {
localStorage.setItem(storage_keys.avatar_style, power_user.avatar_style);
applyAvatarStyle();
}
},
{
key: 'noShadows',
action: async () => {
localStorage.setItem(storage_keys.noShadows, power_user.noShadows);
noShadows();
}
},
{
key: 'chat_width',
action: async () => {
// If chat width is not set, set it to 50
if (!power_user.chat_width) {
power_user.chat_width = 50;
}
localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
applyChatWidth();
}
},
{
key: 'timer_enabled',
action: async () => {
localStorage.setItem(storage_keys.timer_enabled, power_user.timer_enabled);
switchTimer();
}
},
{
key: 'timestamps_enabled',
action: async () => {
localStorage.setItem(storage_keys.timestamps_enabled, power_user.timestamps_enabled);
switchTimestamps();
}
},
{
key: 'timestamp_model_icon',
action: async () => {
localStorage.setItem(storage_keys.timestamp_model_icon, power_user.timestamp_model_icon);
switchIcons();
}
},
{
key: 'mesIDDisplay_enabled',
action: async () => {
localStorage.setItem(storage_keys.mesIDDisplay_enabled, power_user.mesIDDisplay_enabled);
switchMesIDDisplay();
}
},
{
key: 'hotswap_enabled',
action: async () => {
localStorage.setItem(storage_keys.hotswap_enabled, power_user.hotswap_enabled);
switchHotswap();
}
}
];
for (const { key, selector, type, action } of themeProperties) {
if (theme[key] !== undefined) {
power_user[key] = theme[key];
if (selector) $(selector).attr('color', power_user[key]);
if (type) await applyThemeColor(type);
if (action) await action();
} else {
if (selector) { $(selector).attr('color', 'rgba(0,0,0,0)') };
console.debug(`Empty theme key: ${key}`);
power_user[key] = '';
}
}
console.log('theme applied: ' + name);
}
async function applyMovingUIPreset(name) {
resetMovablePanels('quiet')
const movingUIPreset = movingUIPresets.find(x => x.name == name);
if (!movingUIPreset) {
return;
}
power_user.movingUIState = movingUIPreset.movingUIState;
console.log('MovingUI Preset applied: ' + name);
loadMovingUIState()
}
switchUiMode();
applyFontScale();
applyThemeColor();
applyChatWidth();
applyAvatarStyle();
applyBlurStrength();
applyShadowWidth();
switchMovingUI();
noShadows();
switchHotswap();
switchTimer();
switchTimestamps();
switchIcons();
switchMesIDDisplay();
function loadPowerUserSettings(settings, data) {
// Load from settings.json
if (settings.power_user !== undefined) {
Object.assign(power_user, settings.power_user);
}
if (data.themes !== undefined) {
themes = data.themes;
}
if (data.movingUIPresets !== undefined) {
movingUIPresets = data.movingUIPresets;
}
if (data.instruct !== undefined) {
instruct_presets = data.instruct;
}
if (data.context !== undefined) {
context_presets = data.context;
}
// These are still local storage
const fastUi = localStorage.getItem(storage_keys.fast_ui_mode);
const movingUI = localStorage.getItem(storage_keys.movingUI);
const noShadows = localStorage.getItem(storage_keys.noShadows);
const hotswap = localStorage.getItem(storage_keys.hotswap_enabled);
const timer = localStorage.getItem(storage_keys.timer_enabled);
const timestamps = localStorage.getItem(storage_keys.timestamps_enabled);
const mesIDDisplay = localStorage.getItem(storage_keys.mesIDDisplay_enabled);
power_user.fast_ui_mode = fastUi === null ? true : fastUi == "true";
power_user.movingUI = movingUI === null ? false : movingUI == "true";
power_user.noShadows = noShadows === null ? false : noShadows == "true";
power_user.hotswap_enabled = hotswap === null ? true : hotswap == "true";
power_user.timer_enabled = timer === null ? true : timer == "true";
power_user.timestamps_enabled = timestamps === null ? true : timestamps == "true";
power_user.mesIDDisplay_enabled = mesIDDisplay === null ? true : mesIDDisplay == "true";
power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
//power_user.chat_display = Number(localStorage.getItem(storage_keys.chat_display) ?? chat_styles.DEFAULT);
power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
power_user.font_scale = Number(localStorage.getItem(storage_keys.font_scale) ?? 1);
power_user.blur_strength = Number(localStorage.getItem(storage_keys.blur_strength) ?? 10);
if (power_user.chat_display === '') {
power_user.chat_display = chat_styles.DEFAULT;
}
if (power_user.waifuMode === '') {
power_user.waifuMode = false;
}
if (power_user.chat_width === '') {
power_user.chat_width = 50;
}
$('#relaxed_api_urls').prop("checked", power_user.relaxed_api_urls);
$('#trim_spaces').prop("checked", power_user.trim_spaces);
$('#continue_on_send').prop("checked", power_user.continue_on_send);
$('#auto_swipe').prop("checked", power_user.auto_swipe);
$('#auto_swipe_minimum_length').val(power_user.auto_swipe_minimum_length);
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
$('#auto_swipe_blacklist_threshold').val(power_user.auto_swipe_blacklist_threshold);
$('#custom_stopping_strings').val(power_user.custom_stopping_strings);
$("#custom_stopping_strings_macro").prop("checked", power_user.custom_stopping_strings_macro);
$('#fuzzy_search_checkbox').prop("checked", power_user.fuzzy_search);
$('#persona_show_notifications').prop("checked", power_user.persona_show_notifications);
$('#encode_tags').prop("checked", power_user.encode_tags);
$('#lazy_load').val(Number(power_user.lazy_load));
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
$('#auto_scroll_chat_to_bottom').prop("checked", power_user.auto_scroll_chat_to_bottom);
$(`#tokenizer option[value="${power_user.tokenizer}"]`).attr('selected', true);
$(`#pygmalion_formatting option[value=${power_user.pygmalion_formatting}]`).attr("selected", true);
$(`#send_on_enter option[value=${power_user.send_on_enter}]`).attr("selected", true);
$("#import_card_tags").prop("checked", power_user.import_card_tags);
$("#confirm_message_delete").prop("checked", power_user.confirm_message_delete !== undefined ? !!power_user.confirm_message_delete : true);
$("#spoiler_free_mode").prop("checked", power_user.spoiler_free_mode);
$("#collapse-newlines-checkbox").prop("checked", power_user.collapse_newlines);
$("#pin-examples-checkbox").prop("checked", power_user.pin_examples);
$("#always-force-name2-checkbox").prop("checked", power_user.always_force_name2);
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
$('#render_formulas').prop("checked", power_user.render_formulas);
$("#markdown_escape_strings").val(power_user.markdown_escape_strings);
$("#fast_ui_mode").prop("checked", power_user.fast_ui_mode);
$("#waifuMode").prop("checked", power_user.waifuMode);
$("#movingUImode").prop("checked", power_user.movingUI);
$("#noShadowsmode").prop("checked", power_user.noShadows);
$("#start_reply_with").val(power_user.user_prompt_bias);
$("#chat-show-reply-prefix-checkbox").prop("checked", power_user.show_user_prompt_bias);
$("#multigen").prop("checked", power_user.multigen);
$("#multigen_first_chunk").val(power_user.multigen_first_chunk);
$("#multigen_next_chunks").val(power_user.multigen_next_chunks);
$("#play_message_sound").prop("checked", power_user.play_message_sound);
$("#play_sound_unfocused").prop("checked", power_user.play_sound_unfocused);
$("#never_resize_avatars").prop("checked", power_user.never_resize_avatars);
$("#show_card_avatar_urls").prop("checked", power_user.show_card_avatar_urls);
$("#auto_save_msg_edits").prop("checked", power_user.auto_save_msg_edits);
$("#allow_name1_display").prop("checked", power_user.allow_name1_display);
$("#allow_name2_display").prop("checked", power_user.allow_name2_display);
//$("#removeXML").prop("checked", power_user.removeXML);
$("#hotswapEnabled").prop("checked", power_user.hotswap_enabled);
$("#messageTimerEnabled").prop("checked", power_user.timer_enabled);
$("#messageTimestampsEnabled").prop("checked", power_user.timestamps_enabled);
$("#messageModelIconEnabled").prop("checked", power_user.timestamp_model_icon);
$("#mesIDDisplayEnabled").prop("checked", power_user.mesIDDisplay_enabled);
$("#prefer_character_prompt").prop("checked", power_user.prefer_character_prompt);
$("#prefer_character_jailbreak").prop("checked", power_user.prefer_character_jailbreak);
$(`input[name="avatar_style"][value="${power_user.avatar_style}"]`).prop("checked", true);
$(`#chat_display option[value=${power_user.chat_display}]`).attr("selected", true).trigger('change');
$('#chat_width_slider').val(power_user.chat_width);
$("#token_padding").val(power_user.token_padding);
$("#font_scale").val(power_user.font_scale);
$("#font_scale_counter").text(power_user.font_scale);
$("#blur_strength").val(power_user.blur_strength);
$("#blur_strength_counter").text(power_user.blur_strength);
$("#shadow_width").val(power_user.shadow_width);
$("#shadow_width_counter").text(power_user.shadow_width);
$("#main-text-color-picker").attr('color', power_user.main_text_color);
$("#italics-color-picker").attr('color', power_user.italics_text_color);
$("#quote-color-picker").attr('color', power_user.quote_text_color);
//$("#fastui-bg-color-picker").attr('color', power_user.fastui_bg_color);
$("#blur-tint-color-picker").attr('color', power_user.blur_tint_color);
$("#user-mes-blur-tint-color-picker").attr('color', power_user.user_mes_blur_tint_color);
$("#bot-mes-blur-tint-color-picker").attr('color', power_user.bot_mes_blur_tint_color);
$("#shadow-color-picker").attr('color', power_user.shadow_color);
for (const theme of themes) {
const option = document.createElement('option');
option.value = theme.name;
option.innerText = theme.name;
option.selected = theme.name == power_user.theme;
$("#themes").append(option);
}
for (const movingUIPreset of movingUIPresets) {
const option = document.createElement('option');
option.value = movingUIPreset.name;
option.innerText = movingUIPreset.name;
option.selected = movingUIPreset.name == power_user.movingUIPreset;
$("#movingUIPresets").append(option);
}
$(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop("selected", true);
reloadMarkdownProcessor(power_user.render_formulas);
loadInstructMode();
loadContextSettings();
loadMaxContextUnlocked();
switchWaifuMode();
switchSpoilerMode();
loadMovingUIState();
loadCharListState();
}
async function loadCharListState() {
if (document.getElementById('CharID0') !== null) {
console.debug('setting charlist state to...')
if (power_user.charListGrid === true) {
console.debug('..to grid')
$("#charListGridToggle").trigger('click')
} else { console.debug('..to list') }
} else {
console.debug('charlist not ready yet')
await delay(100)
loadCharListState();
}
}
function loadMovingUIState() {
if (isMobile() === false
&& power_user.movingUIState
&& power_user.movingUI === true) {
console.debug('loading movingUI state')
for (var elmntName of Object.keys(power_user.movingUIState)) {
var elmntState = power_user.movingUIState[elmntName];
try {
var elmnt = $('#' + $.escapeSelector(elmntName));
if (elmnt.length) {
console.debug(`loading state for ${elmntName}`)
elmnt.css(elmntState);
} else {
console.debug(`skipping ${elmntName} because it doesn't exist in the DOM`)
}
} catch (err) {
console.debug(`error occurred while processing ${elmntName}: ${err}`)
}
}
} else {
console.debug('skipping movingUI state load')
return
}
}
function loadMaxContextUnlocked() {
$('#max_context_unlocked').prop('checked', power_user.max_context_unlocked);
$('#max_context_unlocked').on('change', function () {
power_user.max_context_unlocked = !!$(this).prop('checked');
switchMaxContextSize();
saveSettingsDebounced();
});
switchMaxContextSize();
}
function switchMaxContextSize() {
const element = $('#max_context');
const maxValue = power_user.max_context_unlocked ? MAX_CONTEXT_UNLOCKED : MAX_CONTEXT_DEFAULT;
element.attr('max', maxValue);
const value = Number(element.val());
if (value >= maxValue) {
element.val(maxValue).trigger('input');
}
}
function loadContextSettings() {
const controls = [
{ id: "context_story_string", property: "story_string", isCheckbox: false },
{ id: "context_example_separator", property: "example_separator", isCheckbox: false },
{ id: "context_chat_start", property: "chat_start", isCheckbox: false },
];
controls.forEach(control => {
const $element = $(`#${control.id}`);
if (control.isCheckbox) {
$element.prop('checked', power_user.context[control.property]);
} else {
$element.val(power_user.context[control.property]);
}
$element.on('input', function () {
power_user.context[control.property] = control.isCheckbox ? !!$(this).prop('checked') : $(this).val();
saveSettingsDebounced();
});
});
context_presets.forEach((preset) => {
const name = preset.name;
const option = document.createElement('option');
option.value = name;
option.innerText = name;
option.selected = name === power_user.context.preset;
$('#context_presets').append(option);
});
$('#context_presets').on('change', function () {
const name = $(this).find(':selected').val();
const preset = context_presets.find(x => x.name === name);
if (!preset) {
return;
}
power_user.context.preset = name;
controls.forEach(control => {
if (preset[control.property] !== undefined) {
power_user.context[control.property] = preset[control.property];
const $element = $(`#${control.id}`);
if (control.isCheckbox) {
$element.prop('checked', power_user.context[control.property]).trigger('input');
} else {
$element.val(power_user.context[control.property]).trigger('input');
}
}
});
});
}
function loadInstructMode() {
const controls = [
{ id: "instruct_enabled", property: "enabled", isCheckbox: true },
{ id: "instruct_wrap", property: "wrap", isCheckbox: true },
{ id: "instruct_system_prompt", property: "system_prompt", isCheckbox: false },
{ id: "instruct_system_sequence", property: "system_sequence", isCheckbox: false },
{ id: "instruct_separator_sequence", property: "separator_sequence", isCheckbox: false },
{ id: "instruct_input_sequence", property: "input_sequence", isCheckbox: false },
{ id: "instruct_output_sequence", property: "output_sequence", isCheckbox: false },
{ id: "instruct_stop_sequence", property: "stop_sequence", isCheckbox: false },
{ id: "instruct_names", property: "names", isCheckbox: true },
{ id: "instruct_macro", property: "macro", isCheckbox: true },
{ id: "instruct_names_force_groups", property: "names_force_groups", isCheckbox: true },
{ id: "instruct_last_output_sequence", property: "last_output_sequence", isCheckbox: false },
];
if (power_user.instruct.names_force_groups === undefined) {
power_user.instruct.names_force_groups = true;
}
controls.forEach(control => {
const $element = $(`#${control.id}`);
if (control.isCheckbox) {
$element.prop('checked', power_user.instruct[control.property]);
} else {
$element.val(power_user.instruct[control.property]);
}
$element.on('input', function () {
power_user.instruct[control.property] = control.isCheckbox ? !!$(this).prop('checked') : $(this).val();
saveSettingsDebounced();
});
});
instruct_presets.forEach((preset) => {
const name = preset.name;
const option = document.createElement('option');
option.value = name;
option.innerText = name;
option.selected = name === power_user.instruct.preset;
$('#instruct_presets').append(option);
});
$('#instruct_presets').on('change', function () {
const name = $(this).find(':selected').val();
const preset = instruct_presets.find(x => x.name === name);
if (!preset) {
return;
}
power_user.instruct.preset = name;
controls.forEach(control => {
if (preset[control.property] !== undefined) {
power_user.instruct[control.property] = preset[control.property];
const $element = $(`#${control.id}`);
if (control.isCheckbox) {
$element.prop('checked', power_user.instruct[control.property]).trigger('input');
} else {
$element.val(power_user.instruct[control.property]).trigger('input');
}
}
});
});
}
export function fuzzySearchCharacters(searchValue) {
const fuse = new Fuse(characters, {
keys: [
{ name: 'data.name', weight: 5 },
{ name: 'data.description', weight: 3 },
{ name: 'data.mes_example', weight: 3 },
{ name: 'data.scenario', weight: 2 },
{ name: 'data.personality', weight: 2 },
{ name: 'data.first_mes', weight: 2 },
{ name: 'data.creator_notes', weight: 2 },
{ name: 'data.creator', weight: 1 },
{ name: 'data.tags', weight: 1 },
{ name: 'data.alternate_greetings', weight: 1 }
],
includeScore: true,
ignoreLocation: true,
threshold: 0.2,
});
const results = fuse.search(searchValue);
console.debug('Characters fuzzy search results for ' + searchValue, results);
const indices = results.map(x => x.refIndex);
return indices;
}
export function fuzzySearchGroups(searchValue) {
const fuse = new Fuse(groups, {
keys: [
{ name: 'name', weight: 3 },
{ name: 'members', weight: 1 },
],
includeScore: true,
ignoreLocation: true,
threshold: 0.2,
});
const results = fuse.search(searchValue);
console.debug('Groups fuzzy search results for ' + searchValue, results);
const ids = results.map(x => String(x.item?.id)).filter(x => x);
return ids;
}
export function renderStoryString(params) {
try {
const compiledTemplate = Handlebars.compile(power_user.context.story_string, { noEscape: true });
let output = compiledTemplate(params);
output = substituteParams(output, params.user, params.char);
output = `${output.trim()}\n`; // add a newline to the end
return output;
} catch (e) {
toastr.error('Check the story string template for validity', 'Error rendering story string');
console.error('Error rendering story string', e);
throw e;
}
}
export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvatar, name1, name2) {
let includeNames = isNarrator ? false : power_user.instruct.names;
if (!isNarrator && power_user.instruct.names_force_groups && (selected_group || forceAvatar)) {
includeNames = true;
}
let sequence = (isUser || isNarrator) ? power_user.instruct.input_sequence : power_user.instruct.output_sequence;
if (power_user.instruct.macro) {
sequence = substituteParams(sequence, name1, name2);
}
const separator = power_user.instruct.wrap ? '\n' : '';
const separatorSequence = power_user.instruct.separator_sequence && !isUser
? power_user.instruct.separator_sequence
: (power_user.instruct.wrap ? '\n' : '');
const textArray = includeNames ? [sequence, `${name}: ${mes}`, separatorSequence] : [sequence, mes, separatorSequence];
const text = textArray.filter(x => x).join(separator);
return text;
}
export function formatInstructStoryString(story, systemPrompt) {
// If the character has a custom system prompt AND user has it preferred, use that instead of the default
systemPrompt = power_user.prefer_character_prompt && systemPrompt ? systemPrompt : power_user.instruct.system_prompt;
const sequence = power_user.instruct.system_sequence || '';
const prompt = substituteParams(systemPrompt, name1, name2, power_user.instruct.system_prompt) || '';
const separator = power_user.instruct.wrap ? '\n' : '';
const textArray = [sequence, prompt + '\n' + story];
const text = textArray.filter(x => x).join(separator);
return text;
}
export function formatInstructModePrompt(name, isImpersonate, promptBias, name1, name2) {
const includeNames = power_user.instruct.names || (!!selected_group && power_user.instruct.names_force_groups);
const getOutputSequence = () => power_user.instruct.last_output_sequence || power_user.instruct.output_sequence;
let sequence = isImpersonate ? power_user.instruct.input_sequence : getOutputSequence();
if (power_user.instruct.macro) {
sequence = substituteParams(sequence, name1, name2);
}
const separator = power_user.instruct.wrap ? '\n' : '';
let text = includeNames ? (separator + sequence + separator + `${name}:`) : (separator + sequence);
if (!isImpersonate && promptBias) {
text += (includeNames ? promptBias : (separator + promptBias));
}
return text.trimEnd();
}
const sortFunc = (a, b) => power_user.sort_order == 'asc' ? compareFunc(a, b) : compareFunc(b, a);
const compareFunc = (first, second) => {
if (power_user.sort_order == 'random') {
return Math.random() > 0.5 ? 1 : -1;
}
switch (power_user.sort_rule) {
case 'boolean':
const a = first[power_user.sort_field];
const b = second[power_user.sort_field];
if (a === true || a === 'true') return 1; // Prioritize 'true' or true
if (b === true || b === 'true') return -1; // Prioritize 'true' or true
if (a && !b) return -1; // Move truthy values to the end
if (!a && b) return 1; // Move falsy values to the beginning
if (a === b) return 0; // Sort equal values normally
return a < b ? -1 : 1; // Sort non-boolean values normally
default:
return typeof first[power_user.sort_field] == "string"
? first[power_user.sort_field].localeCompare(second[power_user.sort_field])
: first[power_user.sort_field] - second[power_user.sort_field];
}
};
function sortEntitiesList(entities) {
if (power_user.sort_field == undefined || entities.length === 0) {
return;
}
entities.sort((a, b) => sortFunc(a.item, b.item));
}
async function saveTheme() {
const name = await callPopup('Enter a theme preset name:', 'input');
if (!name) {
return;
}
const theme = {
name,
blur_strength: power_user.blur_strength,
main_text_color: power_user.main_text_color,
italics_text_color: power_user.italics_text_color,
quote_text_color: power_user.quote_text_color,
//fastui_bg_color: power_user.fastui_bg_color,
blur_tint_color: power_user.blur_tint_color,
user_mes_blur_tint_color: power_user.user_mes_blur_tint_color,
bot_mes_blur_tint_color: power_user.bot_mes_blur_tint_color,
shadow_color: power_user.shadow_color,
shadow_width: power_user.shadow_width,
font_scale: power_user.font_scale,
fast_ui_mode: power_user.fast_ui_mode,
waifuMode: power_user.waifuMode,
avatar_style: power_user.avatar_style,
chat_display: power_user.chat_display,
noShadows: power_user.noShadows,
chat_width: power_user.chat_width,
timer_enabled: power_user.timer_enabled,
timestamps_enabled: power_user.timestamps_enabled,
timestamp_model_icon: power_user.timestamp_model_icon,
mesIDDisplay_enabled: power_user.mesIDDisplay_enabled,
hotswap_enabled: power_user.hotswap_enabled,
};
const response = await fetch('/savetheme', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(theme)
});
if (response.ok) {
const themeIndex = themes.findIndex(x => x.name == name);
if (themeIndex == -1) {
themes.push(theme);
const option = document.createElement('option');
option.selected = true;
option.value = name;
option.innerText = name;
$('#themes').append(option);
}
else {
themes[themeIndex] = theme;
$(`#themes option[value="${name}"]`).attr('selected', true);
}
power_user.theme = name;
saveSettingsDebounced();
}
}
async function saveMovingUI() {
const name = await callPopup('Enter a name for the MovingUI Preset:', 'input');
if (!name) {
return;
}
const movingUIPreset = {
name,
movingUIState: power_user.movingUIState
}
console.log(movingUIPreset)
const response = await fetch('/savemovingui', {
method: 'POST',
headers: getRequestHeaders(),
body: JSON.stringify(movingUIPreset)
});
if (response.ok) {
const movingUIPresetIndex = movingUIPresets.findIndex(x => x.name == name);
if (movingUIPresetIndex == -1) {
movingUIPresets.push(movingUIPreset);
const option = document.createElement('option');
option.selected = true;
option.value = name;
option.innerText = name;
$('#movingUIPresets').append(option);
}
else {
movingUIPresets[movingUIPresetIndex] = movingUIPreset;
$(`#movingUIPresets option[value="${name}"]`).attr('selected', true);
}
power_user.movingUIPreset = name;
saveSettingsDebounced();
} else {
toastr.warning('failed to save MovingUI state.')
}
}
async function resetMovablePanels(type) {
const panelIds = [
'sheld',
'left-nav-panel',
'right-nav-panel',
'WorldInfo',
'floatingPrompt',
'expression-holder',
'groupMemberListPopout'
];
const panelStyles = ['top', 'left', 'right', 'bottom', 'height', 'width', 'margin',];
panelIds.forEach((id) => {
const panel = document.getElementById(id);
if (panel) {
$(panel).addClass('resizing');
panelStyles.forEach((style) => {
panel.style[style] = '';
});
}
});
const zoomedAvatars = document.querySelectorAll('.zoomed_avatar');
if (zoomedAvatars.length > 0) {
zoomedAvatars.forEach((avatar) => {
avatar.classList.add('resizing');
panelStyles.forEach((style) => {
avatar.style[style] = '';
});
});
}
$('[data-dragged="true"]').removeAttr('data-dragged');
await delay(50)
power_user.movingUIState = {};
//if user manually resets panels, deselect the current preset
if (type !== 'quiet' && type !== 'resize') {
power_user.movingUIPreset = 'Default'
$(`#movingUIPresets option[value="Default"]`).prop('selected', true);
}
saveSettingsDebounced();
eventSource.emit(event_types.MOVABLE_PANELS_RESET);
eventSource.once(event_types.SETTINGS_UPDATED, () => {
$(".resizing").removeClass('resizing');
//if happening as part of preset application, do it quietly.
if (type === 'quiet') {
return
//if happening due to resize, tell user.
} else if (type === 'resize') {
toastr.warning('Panel positions reset due to zoom/resize');
//if happening due to manual button press
} else {
toastr.success('Panel positions reset');
}
});
}
function doNewChat() {
setTimeout(() => {
$("#option_start_new_chat").trigger('click');
}, 1);
//$("#dialogue_popup").hide();
setTimeout(() => {
$("#dialogue_popup_ok").trigger('click');
}, 1);
}
function doRandomChat() {
resetSelectedGroup();
setCharacterId(Math.floor(Math.random() * characters.length).toString());
setTimeout(() => {
reloadCurrentChat();
}, 1);
}
async function doMesCut(_, text) {
console.debug(`was asked to cut message id #${text}`)
//reject invalid args or no args
if (text && isNaN(text) || !text) {
toastr.error(`Must enter a single number only, non-number characters disallowed.`)
return
}
let mesIDToCut = Number(text).toFixed(0)
let mesToCut = $("#chat").find(`.mes[mesid=${mesIDToCut}]`)
if (!mesToCut.length) {
toastr.error(`Could not find message with ID: ${mesIDToCut}`)
return
}
setEditedMessageId(mesIDToCut);
mesToCut.find('.mes_edit_delete').trigger('click', { fromSlashCommand: true });
}
async function doDelMode(_, text) {
//first enter delmode
$("#option_delete_mes").trigger('click')
//reject invalid args
if (text && isNaN(text)) {
toastr.warning('Must enter a number or nothing.')
await delay(300) //unsure why 300 is neccessary here, but any shorter and it wont see the delmode UI
$("#dialogue_del_mes_cancel").trigger('click');
return
}
//parse valid args
if (text) {
await delay(300) //same as above, need event signal for 'entered del mode'
console.debug('parsing msgs to del')
let numMesToDel = Number(text).toFixed(0)
let lastMesID = $('.last_mes').attr('mesid')
let oldestMesIDToDel = lastMesID - numMesToDel + 1;
//disallow targeting first message
if (oldestMesIDToDel <= 0) {
oldestMesIDToDel = 1
}
let oldestMesToDel = $('#chat').find(`.mes[mesid=${oldestMesIDToDel}]`)
let oldestDelMesCheckbox = $(oldestMesToDel).find('.del_checkbox');
let newLastMesID = oldestMesIDToDel - 1;
console.debug(`DelMesReport -- numMesToDel: ${numMesToDel}, lastMesID: ${lastMesID}, oldestMesIDToDel:${oldestMesIDToDel}, newLastMesID: ${newLastMesID}`)
oldestDelMesCheckbox.trigger('click');
let trueNumberOfDeletedMessage = lastMesID - oldestMesIDToDel + 1
//await delay(1)
$('#dialogue_del_mes_ok').trigger('click');
toastr.success(`Deleted ${trueNumberOfDeletedMessage} messages.`)
return
}
}
function doResetPanels() {
$("#movingUIreset").trigger('click');
}
function addLanguagesToDropdown() {
$.getJSON('i18n.json', function (data) {
if (!Array.isArray(data?.lang)) {
return;
}
for (const lang of data.lang) {
const option = document.createElement('option');
option.value = lang;
option.innerText = lang;
$('#ui_language_select').append(option);
}
const selectedLanguage = localStorage.getItem(storage_keys.ui_language);
if (selectedLanguage) {
$('#ui_language_select').val(selectedLanguage);
}
});
}
function setAvgBG() {
const bgimg = new Image();
bgimg.src = $('#bg1')
.css('background-image')
.replace(/^url\(['"]?/, '')
.replace(/['"]?\)$/, '');
/* const charAvatar = new Image()
charAvatar.src = $("#avatar_load_preview")
.attr('src')
.replace(/^url\(['"]?/, '')
.replace(/['"]?\)$/, '');
const userAvatar = new Image()
userAvatar.src = $("#user_avatar_block .avatar.selected img")
.attr('src')
.replace(/^url\(['"]?/, '')
.replace(/['"]?\)$/, ''); */
bgimg.onload = function () {
var rgb = getAverageRGB(bgimg);
//console.log(`average color of the bg is:`)
//console.log(rgb);
$("#blur-tint-color-picker").attr('color', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')');
const backgroundColorString = $("#blur-tint-color-picker").attr('color')
.replace('rgba', '')
.replace('rgb', '')
.replace('(', '[')
.replace(')', ']'); //[50, 120, 200, 1]; // Example background color
const backgroundColorArray = JSON.parse(backgroundColorString) //[200, 200, 200, 1]
console.log(backgroundColorArray)
$("#main-text-color-picker").attr('color', getReadableTextColor(backgroundColorArray));
console.log($("#main-text-color-picker").attr('color')); // Output: 'rgba(0, 47, 126, 1)'
}
/* charAvatar.onload = function () {
var rgb = getAverageRGB(charAvatar);
//console.log(`average color of the AI avatar is:`);
//console.log(rgb);
$("#bot-mes-blur-tint-color-picker").attr('color', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')');
}
userAvatar.onload = function () {
var rgb = getAverageRGB(userAvatar);
//console.log(`average color of the user avatar is:`);
//console.log(rgb);
$("#user-mes-blur-tint-color-picker").attr('color', 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')');
} */
function getAverageRGB(imgEl) {
var blockSize = 5, // only visit every 5 pixels
defaultRGB = { r: 0, g: 0, b: 0 }, // for non-supporting envs
canvas = document.createElement('canvas'),
context = canvas.getContext && canvas.getContext('2d'),
data, width, height,
i = -4,
length,
rgb = { r: 0, g: 0, b: 0 },
count = 0;
if (!context) {
return defaultRGB;
}
height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;
context.drawImage(imgEl, 0, 0);
try {
data = context.getImageData(0, 0, width, height);
} catch (e) {
/* security error, img on diff domain */alert('x');
return defaultRGB;
}
length = data.data.length;
while ((i += blockSize * 4) < length) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i + 1];
rgb.b += data.data[i + 2];
}
// ~~ used to floor values
rgb.r = ~~(rgb.r / count);
rgb.g = ~~(rgb.g / count);
rgb.b = ~~(rgb.b / count);
return rgb;
}
function hslToRgb(h, s, l) {
const hueToRgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
if (s === 0) {
return [l, l, l];
}
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
const r = hueToRgb(p, q, h + 1 / 3);
const g = hueToRgb(p, q, h);
const b = hueToRgb(p, q, h - 1 / 3);
return [r * 255, g * 255, b * 255];
}
function rgbToLuminance(r, g, b) {
console.log(r, g, b)
const gammaCorrect = (color) => {
return color <= 0.03928
? color / 12.92
: Math.pow((color + 0.055) / 1.055, 2.4);
};
const rsRGB = r / 255;
const gsRGB = g / 255;
const bsRGB = b / 255;
const rLuminance = gammaCorrect(rsRGB).toFixed(2);
const gLuminance = gammaCorrect(gsRGB).toFixed(2);
const bLuminance = gammaCorrect(bsRGB).toFixed(2);
console.log(`rLum ${rLuminance}, gLum ${gLuminance}, bLum ${bLuminance}`)
return 0.2126 * rLuminance + 0.7152 * gLuminance + 0.0722 * bLuminance;
}
//this version keeps BG and main text in same hue
/* function getReadableTextColor(rgb) {
const [r, g, b] = rgb;
// Convert RGB to HSL
const rgbToHsl = (r, g, b) => {
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const d = max - min;
const l = (max + min) / 2;
if (d === 0) return [0, 0, l];
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
const h = (() => {
switch (max) {
case r:
return (g - b) / d + (g < b ? 6 : 0);
case g:
return (b - r) / d + 2;
case b:
return (r - g) / d + 4;
}
})() / 6;
return [h, s, l];
};
const [h, s, l] = rgbToHsl(r / 255, g / 255, b / 255);
// Calculate appropriate text color based on background color
const targetLuminance = l > 0.5 ? 0.2 : 0.8;
const targetSaturation = s > 0.5 ? s - 0.2 : s + 0.2;
const [rNew, gNew, bNew] = hslToRgb(h, targetSaturation, targetLuminance);
// Return the text color in RGBA format
return `rgba(${rNew.toFixed(0)}, ${gNew.toFixed(0)}, ${bNew.toFixed(0)}, 1)`;
}*/
//this version makes main text complimentary color to BG color
function getReadableTextColor(rgb) {
const [r, g, b] = rgb;
// Convert RGB to HSL
const rgbToHsl = (r, g, b) => {
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const d = max - min;
const l = (max + min) / 2;
if (d === 0) return [0, 0, l];
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
const h = (() => {
switch (max) {
case r:
return (g - b) / d + (g < b ? 6 : 0);
case g:
return (b - r) / d + 2;
case b:
return (r - g) / d + 4;
}
})() / 6;
return [h, s, l];
};
const [h, s, l] = rgbToHsl(r / 255, g / 255, b / 255);
// Calculate complementary color based on background color
const complementaryHue = (h + 0.5) % 1;
const complementarySaturation = s > 0.5 ? s - 0.6 : s + 0.6;
const complementaryLuminance = l > 0.5 ? 0.2 : 0.8;
// Convert complementary color back to RGB
const [rNew, gNew, bNew] = hslToRgb(complementaryHue, complementarySaturation, complementaryLuminance);
// Return the text color in RGBA format
return `rgba(${rNew.toFixed(0)}, ${gNew.toFixed(0)}, ${bNew.toFixed(0)}, 1)`;
}
}
export function getCustomStoppingStrings() {
try {
// Parse the JSON string
const strings = JSON.parse(power_user.custom_stopping_strings);
// Make sure it's an array
if (!Array.isArray(strings)) {
return [];
}
// Make sure all the elements are strings
return strings.filter((s) => typeof s === 'string');
} catch (error) {
// If there's an error, return an empty array
console.warn('Error parsing custom stopping strings:', error);
return [];
}
}
$(document).ready(() => {
$(window).on('resize', async () => {
if (isMobile()) {
return
}
//console.log('Window resized!');
const zoomLevel = Number(window.devicePixelRatio).toFixed(2);
const winWidth = window.innerWidth;
const winHeight = window.innerHeight;
console.debug(`Zoom: ${zoomLevel}, X:${winWidth}, Y:${winHeight}`);
if (Object.keys(power_user.movingUIState).length > 0) {
resetMovablePanels('resize');
}
// Adjust layout and styling here
});
// Settings that go to settings.json
$("#collapse-newlines-checkbox").change(function () {
power_user.collapse_newlines = !!$(this).prop("checked");
saveSettingsDebounced();
});
$("#pygmalion_formatting").change(function (e) {
power_user.pygmalion_formatting = Number($(this).find(":selected").val());
getStatus();
saveSettingsDebounced();
});
$("#pin-examples-checkbox").change(function () {
power_user.pin_examples = !!$(this).prop("checked");
saveSettingsDebounced();
});
// include newline is the child of trim sentences
// if include newline is checked, trim sentences must be checked
// if trim sentences is unchecked, include newline must be unchecked
$("#trim_sentences_checkbox").change(function () {
power_user.trim_sentences = !!$(this).prop("checked");
if (!$(this).prop("checked")) {
$("#include_newline_checkbox").prop("checked", false);
power_user.include_newline = false;
}
saveSettingsDebounced();
});
$("#include_newline_checkbox").change(function () {
power_user.include_newline = !!$(this).prop("checked");
if ($(this).prop("checked")) {
$("#trim_sentences_checkbox").prop("checked", true);
power_user.trim_sentences = true;
}
saveSettingsDebounced();
});
$("#always-force-name2-checkbox").change(function () {
power_user.always_force_name2 = !!$(this).prop("checked");
saveSettingsDebounced();
});
$("#markdown_escape_strings").on('input', function () {
power_user.markdown_escape_strings = $(this).val();
saveSettingsDebounced();
reloadMarkdownProcessor(power_user.render_formulas);
});
$("#start_reply_with").on('input', function () {
power_user.user_prompt_bias = $(this).val();
saveSettingsDebounced();
});
$("#chat-show-reply-prefix-checkbox").change(function () {
power_user.show_user_prompt_bias = !!$(this).prop("checked");
reloadCurrentChat();
saveSettingsDebounced();
})
$("#multigen").change(function () {
power_user.multigen = $(this).prop("checked");
saveSettingsDebounced();
});
// Settings that go to local storage
$("#fast_ui_mode").change(function () {
power_user.fast_ui_mode = $(this).prop("checked");
localStorage.setItem(storage_keys.fast_ui_mode, power_user.fast_ui_mode);
switchUiMode();
});
$("#waifuMode").on('change', () => {
power_user.waifuMode = $('#waifuMode').prop("checked");
saveSettingsDebounced();
switchWaifuMode();
});
$("#movingUImode").change(function () {
power_user.movingUI = $(this).prop("checked");
localStorage.setItem(storage_keys.movingUI, power_user.movingUI);
switchMovingUI();
});
$("#noShadowsmode").change(function () {
power_user.noShadows = $(this).prop("checked");
localStorage.setItem(storage_keys.noShadows, power_user.noShadows);
noShadows();
});
$("#movingUIreset").on('click', resetMovablePanels);
$(`input[name="avatar_style"]`).on('input', function (e) {
power_user.avatar_style = Number(e.target.value);
localStorage.setItem(storage_keys.avatar_style, power_user.avatar_style);
applyAvatarStyle();
});
$("#chat_display").on('change', function () {
console.debug('###CHAT DISPLAY SELECTOR CHANGE###')
const value = $(this).find(':selected').val();
power_user.chat_display = Number(value);
saveSettingsDebounced();
applyChatDisplay();
});
$('#chat_width_slider').on('input', function (e) {
power_user.chat_width = Number(e.target.value);
localStorage.setItem(storage_keys.chat_width, power_user.chat_width);
applyChatWidth();
});
$(`input[name="font_scale"]`).on('input', async function (e) {
power_user.font_scale = Number(e.target.value);
$("#font_scale_counter").text(power_user.font_scale);
localStorage.setItem(storage_keys.font_scale, power_user.font_scale);
await applyFontScale();
});
$(`input[name="blur_strength"]`).on('input', async function (e) {
power_user.blur_strength = Number(e.target.value);
$("#blur_strength_counter").text(power_user.blur_strength);
localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength);
await applyBlurStrength();
});
$(`input[name="shadow_width"]`).on('input', async function (e) {
power_user.shadow_width = Number(e.target.value);
$("#shadow_width_counter").text(power_user.shadow_width);
localStorage.setItem(storage_keys.shadow_width, power_user.shadow_width);
await applyShadowWidth();
});
$("#main-text-color-picker").on('change', (evt) => {
power_user.main_text_color = evt.detail.rgba;
applyThemeColor('main');
saveSettingsDebounced();
});
$("#italics-color-picker").on('change', (evt) => {
power_user.italics_text_color = evt.detail.rgba;
applyThemeColor('italics');
saveSettingsDebounced();
});
$("#quote-color-picker").on('change', (evt) => {
power_user.quote_text_color = evt.detail.rgba;
applyThemeColor('quote');
saveSettingsDebounced();
});
$("#blur-tint-color-picker").on('change', (evt) => {
power_user.blur_tint_color = evt.detail.rgba;
applyThemeColor('blurTint');
saveSettingsDebounced();
});
$("#user-mes-blur-tint-color-picker").on('change', (evt) => {
power_user.user_mes_blur_tint_color = evt.detail.rgba;
applyThemeColor('userMesBlurTint');
saveSettingsDebounced();
});
$("#bot-mes-blur-tint-color-picker").on('change', (evt) => {
power_user.bot_mes_blur_tint_color = evt.detail.rgba;
applyThemeColor('botMesBlurTint');
saveSettingsDebounced();
});
$("#shadow-color-picker").on('change', (evt) => {
power_user.shadow_color = evt.detail.rgba;
applyThemeColor('shadow');
saveSettingsDebounced();
});
$("#themes").on('change', function () {
const themeSelected = $(this).find(':selected').val();
power_user.theme = themeSelected;
applyTheme(themeSelected);
saveSettingsDebounced();
});
$("#movingUIPresets").on('change', async function () {
console.log('saw MUI preset change')
const movingUIPresetSelected = $(this).find(':selected').val();
power_user.movingUIPreset = movingUIPresetSelected;
applyMovingUIPreset(movingUIPresetSelected);
saveSettingsDebounced();
});
$("#ui-preset-save-button").on('click', saveTheme);
$("#movingui-preset-save-button").on('click', saveMovingUI);
$("#never_resize_avatars").on('input', function () {
power_user.never_resize_avatars = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#show_card_avatar_urls").on('input', function () {
power_user.show_card_avatar_urls = !!$(this).prop('checked');
printCharacters();
saveSettingsDebounced();
});
$("#play_message_sound").on('input', function () {
power_user.play_message_sound = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#play_sound_unfocused").on('input', function () {
power_user.play_sound_unfocused = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#auto_save_msg_edits").on('input', function () {
power_user.auto_save_msg_edits = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#character_sort_order").on('change', function () {
power_user.sort_field = $(this).find(":selected").data('field');
power_user.sort_order = $(this).find(":selected").data('order');
power_user.sort_rule = $(this).find(":selected").data('rule');
printCharacters();
saveSettingsDebounced();
});
$("#multigen_first_chunk").on('input', function () {
power_user.multigen_first_chunk = Number($(this).val());
saveSettingsDebounced();
});
$("#multigen_next_chunks").on('input', function () {
power_user.multigen_next_chunks = Number($(this).val());
saveSettingsDebounced();
});
$('#auto_swipe').on('input', function () {
power_user.auto_swipe = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#auto_swipe_blacklist').on('input', function () {
power_user.auto_swipe_blacklist = $(this).val()
.split(",")
.map(str => str.trim())
.filter(str => str);
console.log("power_user.auto_swipe_blacklist", power_user.auto_swipe_blacklist)
saveSettingsDebounced();
});
$('#auto_swipe_minimum_length').on('input', function () {
const number = parseInt($(this).val());
if (!isNaN(number)) {
power_user.auto_swipe_minimum_length = number;
saveSettingsDebounced();
}
});
$('#auto_swipe_blacklist_threshold').on('input', function () {
const number = parseInt($(this).val());
if (!isNaN(number)) {
power_user.auto_swipe_blacklist_threshold = number;
saveSettingsDebounced();
}
});
$('#auto_fix_generated_markdown').on('input', function () {
power_user.auto_fix_generated_markdown = !!$(this).prop('checked');
reloadCurrentChat();
saveSettingsDebounced();
});
$("#console_log_prompts").on('input', function () {
power_user.console_log_prompts = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#auto_scroll_chat_to_bottom').on("input", function () {
power_user.auto_scroll_chat_to_bottom = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#tokenizer").on('change', function () {
const value = $(this).find(':selected').val();
power_user.tokenizer = Number(value);
saveSettingsDebounced();
// Trigger character editor re-tokenize
$("#rm_ch_create_block").trigger('input');
$("#character_popup").trigger('input');
});
$("#send_on_enter").on('change', function () {
const value = $(this).find(':selected').val();
power_user.send_on_enter = Number(value);
saveSettingsDebounced();
});
$("#import_card_tags").on('input', function () {
power_user.import_card_tags = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#confirm_message_delete").on('input', function () {
power_user.confirm_message_delete = !!$(this).prop('checked');
saveSettingsDebounced();
});
$("#render_formulas").on("input", function () {
power_user.render_formulas = !!$(this).prop('checked');
reloadMarkdownProcessor(power_user.render_formulas);
reloadCurrentChat();
saveSettingsDebounced();
});
$("#reload_chat").on('click', function () {
const currentChatId = getCurrentChatId();
if (currentChatId !== undefined && currentChatId !== null) {
reloadCurrentChat();
}
});
$("#allow_name1_display").on("input", function () {
power_user.allow_name1_display = !!$(this).prop('checked');
reloadCurrentChat();
saveSettingsDebounced();
})
$("#allow_name2_display").on("input", function () {
power_user.allow_name2_display = !!$(this).prop('checked');
reloadCurrentChat();
saveSettingsDebounced();
});
$("#token_padding").on("input", function () {
power_user.token_padding = Number($(this).val());
saveSettingsDebounced();
});
$("#messageTimerEnabled").on("input", function () {
const value = !!$(this).prop('checked');
power_user.timer_enabled = value;
localStorage.setItem(storage_keys.timer_enabled, power_user.timer_enabled);
switchTimer();
});
$("#messageTimestampsEnabled").on("input", function () {
const value = !!$(this).prop('checked');
power_user.timestamps_enabled = value;
localStorage.setItem(storage_keys.timestamps_enabled, power_user.timestamps_enabled);
switchTimestamps();
});
$("#messageModelIconEnabled").on("input", function () {
const value = !!$(this).prop('checked');
power_user.timestamp_model_icon = value;
localStorage.setItem(storage_keys.timestamp_model_icon, power_user.timestamp_model_icon);
switchIcons();
});
$("#mesIDDisplayEnabled").on("input", function () {
const value = !!$(this).prop('checked');
power_user.mesIDDisplay_enabled = value;
localStorage.setItem(storage_keys.mesIDDisplay_enabled, power_user.mesIDDisplay_enabled);
switchMesIDDisplay();
});
$("#hotswapEnabled").on("input", function () {
const value = !!$(this).prop('checked');
power_user.hotswap_enabled = value;
localStorage.setItem(storage_keys.hotswap_enabled, power_user.hotswap_enabled);
switchHotswap();
});
$("#prefer_character_prompt").on("input", function () {
const value = !!$(this).prop('checked');
power_user.prefer_character_prompt = value;
saveSettingsDebounced();
});
$("#prefer_character_jailbreak").on("input", function () {
const value = !!$(this).prop('checked');
power_user.prefer_character_jailbreak = value;
saveSettingsDebounced();
});
$("#continue_on_send").on("input", function () {
const value = !!$(this).prop('checked');
power_user.continue_on_send = value;
saveSettingsDebounced();
});
$("#trim_spaces").on("input", function () {
const value = !!$(this).prop('checked');
power_user.trim_spaces = value;
saveSettingsDebounced();
});
$("#relaxed_api_urls").on("input", function () {
const value = !!$(this).prop('checked');
power_user.relaxed_api_urls = value;
saveSettingsDebounced();
});
$('#spoiler_free_mode').on('input', function () {
power_user.spoiler_free_mode = !!$(this).prop('checked');
switchSpoilerMode();
saveSettingsDebounced();
});
$('#spoiler_free_desc_button').on('click', function () {
peekSpoilerMode();
$(this).toggleClass('fa-eye fa-eye-slash');
});
$('#custom_stopping_strings').on('input', function () {
power_user.custom_stopping_strings = $(this).val();
saveSettingsDebounced();
});
$("#custom_stopping_strings_macro").change(function () {
power_user.custom_stopping_strings_macro = !!$(this).prop("checked");
saveSettingsDebounced();
});
$('#fuzzy_search_checkbox').on('input', function () {
power_user.fuzzy_search = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#persona_show_notifications').on('input', function () {
power_user.persona_show_notifications = !!$(this).prop('checked');
saveSettingsDebounced();
});
$('#encode_tags').on('input', async function () {
power_user.encode_tags = !!$(this).prop('checked');
await reloadCurrentChat();
saveSettingsDebounced();
});
$('#lazy_load').on('input', function () {
power_user.lazy_load = Number($(this).val());
saveSettingsDebounced();
});
$('#ui_language_select').on('change', async function () {
const language = $(this).val();
if (language) {
localStorage.setItem(storage_keys.ui_language, language);
} else {
localStorage.removeItem(storage_keys.ui_language);
}
location.reload();
});
$(window).on('focus', function () {
browser_has_focus = true;
});
$(window).on('blur', function () {
browser_has_focus = false;
});
registerSlashCommand('vn', toggleWaifu, [], ' swaps Visual Novel Mode On/Off', false, true);
registerSlashCommand('newchat', doNewChat, ['newchat'], ' start a new chat with current character', true, true);
registerSlashCommand('random', doRandomChat, ['random'], ' start a new chat with a random character', true, true);
registerSlashCommand('delmode', doDelMode, ['del'], '<span class="monospace">(optional number)</span> enter message deletion mode, and auto-deletes N messages if numeric argument is provided', true, true);
registerSlashCommand('cut', doMesCut, [], ' <span class="monospace">(requred number)</span> cuts the specified message from the chat', true, true);
registerSlashCommand('resetpanels', doResetPanels, ['resetui'], ' resets UI panels to original state.', true, true);
registerSlashCommand('bgcol', setAvgBG, [], ' WIP test of auto-bg avg coloring', true, true);
addLanguagesToDropdown();
});