mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into parser-v2
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -45,3 +45,4 @@ access.log
|
||||
/cache/
|
||||
public/css/user.css
|
||||
/plugins/
|
||||
/data
|
||||
|
@ -4519,6 +4519,22 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- various fullscreen popups -->
|
||||
<template id="shadow_popup_template">
|
||||
<div class="shadow_popup">
|
||||
<div class="dialogue_popup">
|
||||
<div class="dialogue_popup_holder">
|
||||
<div class="dialogue_popup_text">
|
||||
<h3 class="margin0">text</h3>
|
||||
</div>
|
||||
<textarea class="dialogue_popup_input text_pole" rows="1"></textarea>
|
||||
<div class="dialogue_popup_controls">
|
||||
<div class="dialogue_popup_ok menu_button" data-i18n="Delete">Delete</div>
|
||||
<div class="dialogue_popup_cancel menu_button" data-i18n="Cancel">Cancel</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div id="shadow_popup">
|
||||
<div id="dialogue_popup">
|
||||
<div id="dialogue_popup_holder">
|
||||
|
@ -211,6 +211,7 @@ import { loadMancerModels, loadOllamaModels, loadTogetherAIModels, loadInfermati
|
||||
import { appendFileContent, hasPendingFileAttachment, populateFileAttachment, decodeStyleTags, encodeStyleTags, isExternalMediaAllowed, getCurrentEntityId } from './scripts/chats.js';
|
||||
import { initPresetManager } from './scripts/preset-manager.js';
|
||||
import { evaluateMacros } from './scripts/macros.js';
|
||||
import { callGenericPopup } from './scripts/popup.js';
|
||||
|
||||
//exporting functions and vars for mods
|
||||
export {
|
||||
@ -821,7 +822,7 @@ let create_save = {
|
||||
//animation right menu
|
||||
export const ANIMATION_DURATION_DEFAULT = 125;
|
||||
export let animation_duration = ANIMATION_DURATION_DEFAULT;
|
||||
let animation_easing = 'ease-in-out';
|
||||
export let animation_easing = 'ease-in-out';
|
||||
let popup_type = '';
|
||||
let chat_file_for_del = '';
|
||||
let online_status = 'no_connection';
|
||||
@ -3466,7 +3467,6 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu
|
||||
// Add persona description to prompt
|
||||
addPersonaDescriptionExtensionPrompt();
|
||||
// Call combined AN into Generate
|
||||
let allAnchors = getAllExtensionPrompts();
|
||||
const beforeScenarioAnchor = getExtensionPrompt(extension_prompt_types.BEFORE_PROMPT).trimStart();
|
||||
const afterScenarioAnchor = getExtensionPrompt(extension_prompt_types.IN_PROMPT);
|
||||
|
||||
@ -3513,10 +3513,11 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu
|
||||
|
||||
function getMessagesTokenCount() {
|
||||
const encodeString = [
|
||||
beforeScenarioAnchor,
|
||||
storyString,
|
||||
afterScenarioAnchor,
|
||||
examplesString,
|
||||
chatString,
|
||||
allAnchors,
|
||||
quiet_prompt,
|
||||
cyclePrompt,
|
||||
userAlignmentMessage,
|
||||
@ -3784,12 +3785,13 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu
|
||||
console.debug('---checking Prompt size');
|
||||
setPromptString();
|
||||
const prompt = [
|
||||
beforeScenarioAnchor,
|
||||
storyString,
|
||||
afterScenarioAnchor,
|
||||
mesExmString,
|
||||
mesSend.map((e) => `${e.extensionPrompts.join('')}${e.message}`).join(''),
|
||||
'\n',
|
||||
generatedPromptCache,
|
||||
allAnchors,
|
||||
quiet_prompt,
|
||||
].join('').replace(/\r/gm, '');
|
||||
let thisPromptContextSize = getTokenCount(prompt, power_user.token_padding);
|
||||
@ -4025,7 +4027,8 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu
|
||||
...thisPromptBits[currentArrayEntry],
|
||||
rawPrompt: generate_data.prompt || generate_data.input,
|
||||
mesId: getNextMessageId(type),
|
||||
allAnchors: allAnchors,
|
||||
allAnchors: getAllExtensionPrompts(),
|
||||
chatInjects: injectedIndices?.map(index => arrMes[arrMes.length - index - 1])?.join('') || '',
|
||||
summarizeString: (extension_prompts['1_memory']?.value || ''),
|
||||
authorsNoteString: (extension_prompts['2_floating_prompt']?.value || ''),
|
||||
smartContextString: (extension_prompts['chromadb']?.value || ''),
|
||||
@ -4649,8 +4652,13 @@ function promptItemize(itemizedPrompts, requestedMesId) {
|
||||
zeroDepthAnchorTokens: getTokenCount(itemizedPrompts[thisPromptSet].zeroDepthAnchor), // TODO: unused
|
||||
thisPrompt_padding: itemizedPrompts[thisPromptSet].padding,
|
||||
this_main_api: itemizedPrompts[thisPromptSet].main_api,
|
||||
chatInjects: getTokenCount(itemizedPrompts[thisPromptSet].chatInjects),
|
||||
};
|
||||
|
||||
if (params.chatInjects){
|
||||
params.ActualChatHistoryTokens = params.ActualChatHistoryTokens - params.chatInjects;
|
||||
}
|
||||
|
||||
if (params.this_main_api == 'openai') {
|
||||
//for OAI API
|
||||
//console.log('-- Counting OAI Tokens');
|
||||
@ -7808,6 +7816,7 @@ window['SillyTavern'].getContext = function () {
|
||||
registedDebugFunction: registerDebugFunction,
|
||||
renderExtensionTemplate: renderExtensionTemplate,
|
||||
callPopup: callPopup,
|
||||
callGenericPopup: callGenericPopup,
|
||||
mainApi: main_api,
|
||||
extensionSettings: extension_settings,
|
||||
ModuleWorkerWrapper: ModuleWorkerWrapper,
|
||||
|
@ -181,6 +181,7 @@ function downloadAssetsList(url) {
|
||||
const displayName = DOMPurify.sanitize(asset['name'] || asset['id']);
|
||||
const description = DOMPurify.sanitize(asset['description'] || '');
|
||||
const url = isValidUrl(asset['url']) ? asset['url'] : '';
|
||||
const title = assetType === 'extension' ? `Extension repo/guide: ${url}` : 'Preview in browser';
|
||||
const previewIcon = (assetType === 'extension' || assetType === 'character') ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple';
|
||||
|
||||
const assetBlock = $('<i></i>')
|
||||
@ -188,7 +189,7 @@ function downloadAssetsList(url) {
|
||||
.append(`<div class="flex-container flexFlowColumn flexNoGap">
|
||||
<span class="asset-name flex-container alignitemscenter">
|
||||
<b>${displayName}</b>
|
||||
<a class="asset_preview" href="${url}" target="_blank" title="Extension repo/guide: ${url}">
|
||||
<a class="asset_preview" href="${url}" target="_blank" title="${title}">
|
||||
<i class="fa-solid fa-sm ${previewIcon}"></i>
|
||||
</a>
|
||||
</span>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { callPopup } from '../../../../script.js';
|
||||
import { POPUP_TYPE, Popup } from '../../../popup.js';
|
||||
import { setSlashCommandAutoComplete } from '../../../slash-commands.js';
|
||||
import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
|
||||
import { getSortableDelay } from '../../../utils.js';
|
||||
@ -46,6 +46,8 @@ export class QuickReply {
|
||||
/**@type {HTMLInputElement}*/ settingsDomLabel;
|
||||
/**@type {HTMLTextAreaElement}*/ settingsDomMessage;
|
||||
|
||||
/**@type {Popup}*/ editorPopup;
|
||||
|
||||
/**@type {HTMLElement}*/ editorExecuteBtn;
|
||||
/**@type {HTMLElement}*/ editorExecuteErrors;
|
||||
/**@type {HTMLInputElement}*/ editorExecuteHide;
|
||||
@ -199,7 +201,8 @@ export class QuickReply {
|
||||
/**@type {HTMLElement} */
|
||||
// @ts-ignore
|
||||
const dom = this.template.cloneNode(true);
|
||||
const popupResult = callPopup(dom, 'text', undefined, { okButton: 'OK', wide: true, large: true, rows: 1 });
|
||||
this.editorPopup = new Popup(dom, POPUP_TYPE.TEXT, undefined, { okButton: 'OK', wide: true, large: true, rows: 1 });
|
||||
const popupResult = this.editorPopup.show();
|
||||
|
||||
// basics
|
||||
/**@type {HTMLInputElement}*/
|
||||
@ -446,7 +449,7 @@ export class QuickReply {
|
||||
this.editorExecuteBtn.classList.add('qr--busy');
|
||||
this.editorExecuteErrors.innerHTML = '';
|
||||
if (this.editorExecuteHide.checked) {
|
||||
document.querySelector('#shadow_popup').classList.add('qr--hide');
|
||||
this.editorPopup.dom.classList.add('qr--hide');
|
||||
}
|
||||
try {
|
||||
this.editorExecutePromise = this.execute();
|
||||
@ -456,7 +459,7 @@ export class QuickReply {
|
||||
}
|
||||
this.editorExecutePromise = null;
|
||||
this.editorExecuteBtn.classList.remove('qr--busy');
|
||||
document.querySelector('#shadow_popup').classList.remove('qr--hide');
|
||||
this.editorPopup.dom.classList.remove('qr--hide');
|
||||
}
|
||||
|
||||
|
||||
|
@ -216,60 +216,60 @@
|
||||
align-items: baseline;
|
||||
}
|
||||
@media screen and (max-width: 750px) {
|
||||
body #dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor {
|
||||
body .dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor {
|
||||
flex-direction: column;
|
||||
}
|
||||
body #dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels {
|
||||
body .dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels {
|
||||
flex-direction: column;
|
||||
}
|
||||
body #dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > #qr--modal-message {
|
||||
body .dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > #qr--modal-message {
|
||||
min-height: 90svh;
|
||||
}
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) {
|
||||
.dialogue_popup:has(#qr--modalEditor) {
|
||||
aspect-ratio: unset;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1em;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5em;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label {
|
||||
flex: 1 1 1px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label > .qr--labelText {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label > .qr--labelText {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label > .qr--labelHint {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label > .qr--labelHint {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label > input {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--labels > label > input {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 1em;
|
||||
@ -277,24 +277,24 @@
|
||||
font-size: smaller;
|
||||
align-items: baseline;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings > .checkbox_label {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings > .checkbox_label {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings > .checkbox_label > input {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings > .checkbox_label > input {
|
||||
font-size: inherit;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > #qr--modal-message {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > #qr--modal-message {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor #qr--modal-execute {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor #qr--modal-execute {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 0.5em;
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor #qr--modal-execute.qr--busy {
|
||||
.dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor #qr--modal-execute.qr--busy {
|
||||
opacity: 0.5;
|
||||
cursor: wait;
|
||||
}
|
||||
#shadow_popup.qr--hide {
|
||||
.shadow_popup.qr--hide {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
@ -242,7 +242,7 @@
|
||||
|
||||
|
||||
@media screen and (max-width: 750px) {
|
||||
body #dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor {
|
||||
body .dialogue_popup:has(#qr--modalEditor) .dialogue_popup_text > #qr--modalEditor {
|
||||
flex-direction: column;
|
||||
> #qr--main > .qr--labels {
|
||||
flex-direction: column;
|
||||
@ -252,10 +252,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
#dialogue_popup:has(#qr--modalEditor) {
|
||||
.dialogue_popup:has(#qr--modalEditor) {
|
||||
aspect-ratio: unset;
|
||||
|
||||
#dialogue_popup_text {
|
||||
.dialogue_popup_text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -326,6 +326,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#shadow_popup.qr--hide {
|
||||
.shadow_popup.qr--hide {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
@ -19,8 +19,9 @@ const UPDATE_INTERVAL = 1000;
|
||||
|
||||
let voiceMapEntries = [];
|
||||
let voiceMap = {}; // {charName:voiceid, charName2:voiceid2}
|
||||
let storedvalue = false;
|
||||
let talkingHeadState = false;
|
||||
let lastChatId = null;
|
||||
let lastMessage = null;
|
||||
let lastMessageHash = null;
|
||||
|
||||
const DEFAULT_VOICE_MARKER = '[Default Voice]';
|
||||
@ -67,7 +68,7 @@ export function getPreviewString(lang) {
|
||||
return previewStrings[lang] ?? fallbackPreview;
|
||||
}
|
||||
|
||||
let ttsProviders = {
|
||||
const ttsProviders = {
|
||||
ElevenLabs: ElevenLabsTtsProvider,
|
||||
Silero: SileroTtsProvider,
|
||||
XTTSv2: XTTSTtsProvider,
|
||||
@ -82,7 +83,6 @@ let ttsProviders = {
|
||||
let ttsProvider;
|
||||
let ttsProviderName;
|
||||
|
||||
let ttsLastMessage = null;
|
||||
|
||||
async function onNarrateOneMessage() {
|
||||
audioElement.src = '/sounds/silence.mp3';
|
||||
@ -130,103 +130,13 @@ async function onNarrateText(args, text) {
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
// Primarily determining when to add new chat to the TTS queue
|
||||
const enabled = $('#tts_enabled').is(':checked');
|
||||
$('body').toggleClass('tts', enabled);
|
||||
if (!enabled) {
|
||||
if (!extension_settings.tts.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
const chat = context.chat;
|
||||
|
||||
processTtsQueue();
|
||||
processAudioJobQueue();
|
||||
updateUiAudioPlayState();
|
||||
|
||||
// Auto generation is disabled
|
||||
if (extension_settings.tts.auto_generation == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no characters or group selected
|
||||
if (!context.groupId && context.characterId === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chat changed
|
||||
if (
|
||||
context.chatId !== lastChatId
|
||||
) {
|
||||
currentMessageNumber = context.chat.length ? context.chat.length : 0;
|
||||
saveLastValues();
|
||||
|
||||
// Force to speak on the first message in the new chat
|
||||
if (context.chat.length === 1) {
|
||||
lastMessageHash = -1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// take the count of messages
|
||||
let lastMessageNumber = context.chat.length ? context.chat.length : 0;
|
||||
|
||||
// There's no new messages
|
||||
let diff = lastMessageNumber - currentMessageNumber;
|
||||
let hashNew = getStringHash((chat.length && chat[chat.length - 1].mes) ?? '');
|
||||
|
||||
// if messages got deleted, diff will be < 0
|
||||
if (diff < 0) {
|
||||
// necessary actions will be taken by the onChatDeleted() handler
|
||||
return;
|
||||
}
|
||||
|
||||
// if no new messages, or same message, or same message hash, do nothing
|
||||
if (diff == 0 && hashNew === lastMessageHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If streaming, wait for streaming to finish before processing new messages
|
||||
if (context.streamingProcessor && !context.streamingProcessor.isFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
// clone message object, as things go haywire if message object is altered below (it's passed by reference)
|
||||
const message = structuredClone(chat[chat.length - 1]);
|
||||
|
||||
// if last message within current message, message got extended. only send diff to TTS.
|
||||
if (ttsLastMessage !== null && message.mes.indexOf(ttsLastMessage) !== -1) {
|
||||
let tmp = message.mes;
|
||||
message.mes = message.mes.replace(ttsLastMessage, '');
|
||||
ttsLastMessage = tmp;
|
||||
} else {
|
||||
ttsLastMessage = message.mes;
|
||||
}
|
||||
|
||||
// We're currently swiping. Don't generate voice
|
||||
if (!message || message.mes === '...' || message.mes === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't generate if message doesn't have a display text
|
||||
if (extension_settings.tts.narrate_translated_only && !(message?.extra?.display_text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't generate if message is a user message and user message narration is disabled
|
||||
if (message.is_user && !extension_settings.tts.narrate_user) {
|
||||
return;
|
||||
}
|
||||
|
||||
// New messages, add new chat to history
|
||||
lastMessageHash = hashNew;
|
||||
currentMessageNumber = lastMessageNumber;
|
||||
|
||||
console.debug(
|
||||
`Adding message from ${message.name} for TTS processing: "${message.mes}"`,
|
||||
);
|
||||
ttsJobQueue.push(message);
|
||||
}
|
||||
|
||||
function talkingAnimation(switchValue) {
|
||||
@ -238,11 +148,11 @@ function talkingAnimation(switchValue) {
|
||||
const apiUrl = getApiUrl();
|
||||
const animationType = switchValue ? 'start' : 'stop';
|
||||
|
||||
if (switchValue !== storedvalue) {
|
||||
if (switchValue !== talkingHeadState) {
|
||||
try {
|
||||
console.log(animationType + ' Talking Animation');
|
||||
doExtrasFetch(`${apiUrl}/api/talkinghead/${animationType}_talking`);
|
||||
storedvalue = switchValue; // Update the storedvalue to the current switchValue
|
||||
talkingHeadState = switchValue;
|
||||
} catch (error) {
|
||||
// Handle the error here or simply ignore it to prevent logging
|
||||
}
|
||||
@ -289,7 +199,6 @@ function debugTtsPlayback() {
|
||||
{
|
||||
'ttsProviderName': ttsProviderName,
|
||||
'voiceMap': voiceMap,
|
||||
'currentMessageNumber': currentMessageNumber,
|
||||
'audioPaused': audioPaused,
|
||||
'audioJobQueue': audioJobQueue,
|
||||
'currentAudioJob': currentAudioJob,
|
||||
@ -477,21 +386,12 @@ async function processAudioJobQueue() {
|
||||
|
||||
let ttsJobQueue = [];
|
||||
let currentTtsJob; // Null if nothing is currently being processed
|
||||
let currentMessageNumber = 0;
|
||||
|
||||
function completeTtsJob() {
|
||||
console.info(`Current TTS job for ${currentTtsJob?.name} completed.`);
|
||||
currentTtsJob = null;
|
||||
}
|
||||
|
||||
function saveLastValues() {
|
||||
const context = getContext();
|
||||
lastChatId = context.chatId;
|
||||
lastMessageHash = getStringHash(
|
||||
(context.chat.length && context.chat[context.chat.length - 1].mes) ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
async function tts(text, voiceId, char) {
|
||||
async function processResponse(response) {
|
||||
// RVC injection
|
||||
@ -764,26 +664,103 @@ async function onChatChanged() {
|
||||
await resetTtsPlayback();
|
||||
const voiceMapInit = initVoiceMap();
|
||||
await Promise.race([voiceMapInit, delay(1000)]);
|
||||
ttsLastMessage = null;
|
||||
lastMessage = null;
|
||||
}
|
||||
|
||||
async function onChatDeleted() {
|
||||
async function onMessageEvent(messageId) {
|
||||
// If TTS is disabled, do nothing
|
||||
if (!extension_settings.tts.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto generation is disabled
|
||||
if (!extension_settings.tts.auto_generation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
|
||||
// no characters or group selected
|
||||
if (!context.groupId && context.characterId === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chat changed
|
||||
if (context.chatId !== lastChatId) {
|
||||
lastChatId = context.chatId;
|
||||
lastMessageHash = getStringHash(context.chat[messageId]?.mes ?? '');
|
||||
|
||||
// Force to speak on the first message in the new chat
|
||||
if (context.chat.length === 1) {
|
||||
lastMessageHash = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// clone message object, as things go haywire if message object is altered below (it's passed by reference)
|
||||
const message = structuredClone(context.chat[messageId]);
|
||||
const hashNew = getStringHash(message?.mes ?? '');
|
||||
|
||||
// if no new messages, or same message, or same message hash, do nothing
|
||||
if (hashNew === lastMessageHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isLastMessageInCurrent = () =>
|
||||
lastMessage &&
|
||||
typeof lastMessage === 'object' &&
|
||||
message.swipe_id === lastMessage.swipe_id &&
|
||||
message.name === lastMessage.name &&
|
||||
message.is_user === lastMessage.is_user &&
|
||||
message.mes.indexOf(lastMessage.mes) !== -1;
|
||||
|
||||
// if last message within current message, message got extended. only send diff to TTS.
|
||||
if (isLastMessageInCurrent()) {
|
||||
const tmp = structuredClone(message);
|
||||
message.mes = message.mes.replace(lastMessage.mes, '');
|
||||
lastMessage = tmp;
|
||||
} else {
|
||||
lastMessage = structuredClone(message);
|
||||
}
|
||||
|
||||
// We're currently swiping. Don't generate voice
|
||||
if (!message || message.mes === '...' || message.mes === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't generate if message doesn't have a display text
|
||||
if (extension_settings.tts.narrate_translated_only && !(message?.extra?.display_text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't generate if message is a user message and user message narration is disabled
|
||||
if (message.is_user && !extension_settings.tts.narrate_user) {
|
||||
return;
|
||||
}
|
||||
|
||||
// New messages, add new chat to history
|
||||
lastMessageHash = hashNew;
|
||||
lastChatId = context.chatId;
|
||||
|
||||
console.debug(`Adding message from ${message.name} for TTS processing: "${message.mes}"`);
|
||||
ttsJobQueue.push(message);
|
||||
}
|
||||
|
||||
async function onMessageDeleted() {
|
||||
const context = getContext();
|
||||
|
||||
// update internal references to new last message
|
||||
lastChatId = context.chatId;
|
||||
currentMessageNumber = context.chat.length ? context.chat.length : 0;
|
||||
|
||||
// compare against lastMessageHash. If it's the same, we did not delete the last chat item, so no need to reset tts queue
|
||||
let messageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1].mes) ?? '');
|
||||
const messageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1].mes) ?? '');
|
||||
if (messageHash === lastMessageHash) {
|
||||
return;
|
||||
}
|
||||
lastMessageHash = messageHash;
|
||||
ttsLastMessage = (context.chat.length && context.chat[context.chat.length - 1].mes) ?? '';
|
||||
lastMessage = context.chat.length ? structuredClone(context.chat[context.chat.length - 1]) : null;
|
||||
|
||||
// stop any tts playback since message might not exist anymore
|
||||
await resetTtsPlayback();
|
||||
resetTtsPlayback();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1079,8 +1056,10 @@ $(document).ready(function () {
|
||||
setInterval(wrapper.update.bind(wrapper), UPDATE_INTERVAL); // Init depends on all the things
|
||||
eventSource.on(event_types.MESSAGE_SWIPED, resetTtsPlayback);
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
eventSource.on(event_types.MESSAGE_DELETED, onChatDeleted);
|
||||
eventSource.on(event_types.MESSAGE_DELETED, onMessageDeleted);
|
||||
eventSource.on(event_types.GROUP_UPDATED, onChatChanged);
|
||||
eventSource.on(event_types.MESSAGE_SENT, onMessageEvent);
|
||||
eventSource.on(event_types.MESSAGE_RECEIVED, onMessageEvent);
|
||||
registerSlashCommand('speak', onNarrateText, ['narrate', 'tts'], '<span class="monospace">(text)</span> – narrate any text using currently selected character\'s voice. Use voice="Character Name" argument to set other voice from the voice map, example: <tt>/speak voice="Donald Duck" Quack!</tt>', true, true);
|
||||
document.body.appendChild(audioElement);
|
||||
});
|
||||
|
@ -353,14 +353,30 @@ export function getGroupCharacterCards(groupId, characterId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Runs the macro engine on a text, with custom <FIELDNAME> replace @param {string} value @param {string} characterName @param {string} fieldName @returns {string} */
|
||||
/**
|
||||
* Runs the macro engine on a text, with custom <FIELDNAME> replace
|
||||
* @param {string} value Value to replace
|
||||
* @param {string} fieldName Name of the field
|
||||
* @param {string} characterName Name of the character
|
||||
* @returns {string} Replaced text
|
||||
* */
|
||||
function customBaseChatReplace(value, fieldName, characterName) {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// We should do the custom field name replacement first, and then run it through the normal macro engine with provided names
|
||||
value = value.replace(/<FIELDNAME>/gi, fieldName);
|
||||
return baseChatReplace(value.trim(), name1, characterName);
|
||||
}
|
||||
|
||||
/** Prepares text with prefix/suffix for a character field @param {string} value @param {string} characterName @param {string} fieldName @returns {string} */
|
||||
/**
|
||||
* Prepares text with prefix/suffix for a character field
|
||||
* @param {string} value Value to replace
|
||||
* @param {string} characterName Name of the character
|
||||
* @param {string} fieldName Name of the field
|
||||
* @returns {string} Prepared text
|
||||
* */
|
||||
function replaceAndPrepareForJoin(value, characterName, fieldName) {
|
||||
value = value.trim();
|
||||
if (!value) {
|
||||
|
@ -372,7 +372,7 @@ export function formatInstructModeSystemPrompt(systemPrompt) {
|
||||
* @returns {string[]} Formatted example messages string.
|
||||
*/
|
||||
export function formatInstructModeExamples(mesExamplesArray, name1, name2) {
|
||||
const blockHeading = power_user.context.example_separator ? power_user.context.example_separator + '\n' : '';
|
||||
const blockHeading = power_user.context.example_separator ? `${substituteParams(power_user.context.example_separator)}\n` : '';
|
||||
|
||||
if (power_user.instruct.skip_examples) {
|
||||
return mesExamplesArray.map(x => x.replace(/<START>\n/i, blockHeading));
|
||||
@ -518,13 +518,16 @@ function selectMatchingContextTemplate(name) {
|
||||
/**
|
||||
* Replaces instruct mode macros in the given input string.
|
||||
* @param {string} input Input string.
|
||||
* @param {Object<string, *>} env - Map of macro names to the values they'll be substituted with. If the param
|
||||
* values are functions, those functions will be called and their return values are used.
|
||||
* @returns {string} String with macros replaced.
|
||||
*/
|
||||
export function replaceInstructMacros(input) {
|
||||
export function replaceInstructMacros(input, env) {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
const instructMacros = {
|
||||
'systemPrompt': (power_user.prefer_character_prompt && env.charPrompt ? env.charPrompt : power_user.instruct.system_prompt),
|
||||
'instructSystem|instructSystemPrompt': power_user.instruct.system_prompt,
|
||||
'instructSystemPromptPrefix': power_user.instruct.system_sequence_prefix,
|
||||
'instructSystemPromptSuffix': power_user.instruct.system_sequence_suffix,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { chat, main_api, getMaxContextSize, getCurrentChatId } from '../script.js';
|
||||
import { chat, chat_metadata, main_api, getMaxContextSize, getCurrentChatId } from '../script.js';
|
||||
import { timestampToMoment, isDigitsOnly, getStringHash } from './utils.js';
|
||||
import { textgenerationwebui_banned_in_macros } from './textgen-settings.js';
|
||||
import { replaceInstructMacros } from './instruct-mode.js';
|
||||
@ -7,6 +7,25 @@ import { replaceVariableMacros } from './variables.js';
|
||||
// Register any macro that you want to leave in the compiled story string
|
||||
Handlebars.registerHelper('trim', () => '{{trim}}');
|
||||
|
||||
/**
|
||||
* Gets a hashed id of the current chat from the metadata.
|
||||
* If no metadata exists, creates a new hash and saves it.
|
||||
* @returns {number} The hashed chat id
|
||||
*/
|
||||
function getChatIdHash() {
|
||||
const cachedIdHash = chat_metadata['chat_id_hash'];
|
||||
|
||||
// If chat_id_hash is not already set, calculate it
|
||||
if (!cachedIdHash) {
|
||||
// Use the main_chat if it's available, otherwise get the current chat ID
|
||||
const chatId = chat_metadata['main_chat'] ?? getCurrentChatId();
|
||||
const chatIdHash = getStringHash(chatId);
|
||||
chat_metadata['chat_id_hash'] = chatIdHash;
|
||||
return chatIdHash;
|
||||
}
|
||||
|
||||
return cachedIdHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of the last message in the chat
|
||||
@ -186,7 +205,10 @@ function randomReplace(input, emptyListPlaceholder = '') {
|
||||
|
||||
function pickReplace(input, rawContent, emptyListPlaceholder = '') {
|
||||
const pickPattern = /{{pick\s?::?([^}]+)}}/gi;
|
||||
const chatIdHash = getStringHash(getCurrentChatId());
|
||||
|
||||
// We need to have a consistent chat hash, otherwise we'll lose rolls on chat file rename or branch switches
|
||||
// No need to save metadata here - branching and renaming will implicitly do the save for us, and until then loading it like this is consistent
|
||||
const chatIdHash = getChatIdHash();
|
||||
const rawContentHash = getStringHash(rawContent);
|
||||
|
||||
return input.replace(pickPattern, (match, listString, offset) => {
|
||||
@ -257,7 +279,7 @@ export function evaluateMacros(content, env) {
|
||||
}
|
||||
|
||||
content = diceRollReplace(content);
|
||||
content = replaceInstructMacros(content);
|
||||
content = replaceInstructMacros(content, env);
|
||||
content = replaceVariableMacros(content);
|
||||
content = content.replace(/{{newline}}/gi, '\n');
|
||||
content = content.replace(/\n*{{trim}}\n*/gi, '');
|
||||
|
@ -431,15 +431,15 @@ function convertChatCompletionToInstruct(messages, type) {
|
||||
const exampleMessages = messages.filter(x => x.role === 'system' && (x.name === 'example_user' || x.name === 'example_assistant'));
|
||||
|
||||
if (exampleMessages.length) {
|
||||
examplesText = power_user.context.example_separator + '\n';
|
||||
examplesText += exampleMessages.map(toString).join('\n');
|
||||
examplesText = formatInstructModeExamples(examplesText, name1, name2);
|
||||
const blockHeading = power_user.context.example_separator ? (substituteParams(power_user.context.example_separator) + '\n') : '';
|
||||
const examplesArray = exampleMessages.map(m => '<START>\n' + toString(m));
|
||||
examplesText = blockHeading + formatInstructModeExamples(examplesArray, name1, name2).join('');
|
||||
}
|
||||
|
||||
const chatMessages = messages.slice(firstChatMessage);
|
||||
|
||||
if (chatMessages.length) {
|
||||
chatMessagesText = power_user.context.chat_start + '\n';
|
||||
chatMessagesText = substituteParams(power_user.context.chat_start) + '\n';
|
||||
|
||||
for (const message of chatMessages) {
|
||||
const name = getPrefix(message);
|
||||
|
225
public/scripts/popup.js
Normal file
225
public/scripts/popup.js
Normal file
@ -0,0 +1,225 @@
|
||||
import { animation_duration, animation_easing } from '../script.js';
|
||||
import { delay } from './utils.js';
|
||||
|
||||
|
||||
|
||||
/**@readonly*/
|
||||
/**@enum {Number}*/
|
||||
export const POPUP_TYPE = {
|
||||
'TEXT': 1,
|
||||
'CONFIRM': 2,
|
||||
'INPUT': 3,
|
||||
};
|
||||
|
||||
/**@readonly*/
|
||||
/**@enum {Boolean}*/
|
||||
export const POPUP_RESULT = {
|
||||
'AFFIRMATIVE': true,
|
||||
'NEGATIVE': false,
|
||||
'CANCELLED': undefined,
|
||||
};
|
||||
|
||||
|
||||
|
||||
export class Popup {
|
||||
/**@type {POPUP_TYPE}*/ type;
|
||||
|
||||
/**@type {HTMLElement}*/ dom;
|
||||
/**@type {HTMLElement}*/ dlg;
|
||||
/**@type {HTMLElement}*/ text;
|
||||
/**@type {HTMLTextAreaElement}*/ input;
|
||||
/**@type {HTMLElement}*/ ok;
|
||||
/**@type {HTMLElement}*/ cancel;
|
||||
|
||||
/**@type {POPUP_RESULT}*/ result;
|
||||
/**@type {any}*/ value;
|
||||
|
||||
/**@type {Promise}*/ promise;
|
||||
/**@type {Function}*/ resolver;
|
||||
|
||||
/**@type {Function}*/ keyListenerBound;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {{okButton?: string, cancelButton?: string, rows?: number, wide?: boolean, large?: boolean, allowHorizontalScrolling?: boolean, allowVerticalScrolling?: boolean }} PopupOptions - Options for the popup.
|
||||
* @param {JQuery<HTMLElement>|string|Element} text - Text to display in the popup.
|
||||
* @param {POPUP_TYPE} type - One of Popup.TYPE
|
||||
* @param {string} inputValue - Value to set the input to.
|
||||
* @param {PopupOptions} options - Options for the popup.
|
||||
*/
|
||||
constructor(text, type, inputValue = '', { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling } = {}) {
|
||||
this.type = type;
|
||||
|
||||
/**@type {HTMLTemplateElement}*/
|
||||
const template = document.querySelector('#shadow_popup_template');
|
||||
// @ts-ignore
|
||||
this.dom = template.content.cloneNode(true).querySelector('.shadow_popup');
|
||||
const dlg = this.dom.querySelector('.dialogue_popup');
|
||||
// @ts-ignore
|
||||
this.dlg = dlg;
|
||||
this.text = this.dom.querySelector('.dialogue_popup_text');
|
||||
this.input = this.dom.querySelector('.dialogue_popup_input');
|
||||
this.ok = this.dom.querySelector('.dialogue_popup_ok');
|
||||
this.cancel = this.dom.querySelector('.dialogue_popup_cancel');
|
||||
|
||||
if (wide) dlg.classList.add('wide_dialogue_popup');
|
||||
if (large) dlg.classList.add('large_dialogue_popup');
|
||||
if (allowHorizontalScrolling) dlg.classList.add('horizontal_scrolling_dialogue_popup');
|
||||
if (allowVerticalScrolling) dlg.classList.add('vertical_scrolling_dialogue_popup');
|
||||
|
||||
this.ok.textContent = okButton ?? 'OK';
|
||||
this.cancel.textContent = cancelButton ?? 'Cancel';
|
||||
|
||||
switch(type) {
|
||||
case POPUP_TYPE.TEXT: {
|
||||
this.input.style.display = 'none';
|
||||
this.cancel.style.display = 'none';
|
||||
break;
|
||||
}
|
||||
case POPUP_TYPE.CONFIRM: {
|
||||
this.input.style.display = 'none';
|
||||
this.ok.textContent = okButton ?? 'Yes';
|
||||
this.cancel.textContent = cancelButton ?? 'No';
|
||||
break;
|
||||
}
|
||||
case POPUP_TYPE.INPUT: {
|
||||
this.input.style.display = 'block';
|
||||
this.ok.textContent = okButton ?? 'Save';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// illegal argument
|
||||
}
|
||||
}
|
||||
|
||||
this.input.value = inputValue;
|
||||
this.input.rows = rows ?? 1;
|
||||
|
||||
this.text.innerHTML = '';
|
||||
if (text instanceof jQuery) {
|
||||
$(this.text).append(text);
|
||||
} else if (text instanceof HTMLElement) {
|
||||
this.text.append(text);
|
||||
} else if (typeof text == 'string') {
|
||||
this.text.innerHTML = text;
|
||||
} else {
|
||||
// illegal argument
|
||||
}
|
||||
|
||||
this.ok.addEventListener('click', ()=>this.completeAffirmative());
|
||||
this.cancel.addEventListener('click', ()=>this.completeNegative());
|
||||
const keyListener = (evt)=>{
|
||||
switch (evt.key) {
|
||||
case 'Escape': {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.completeCancelled();
|
||||
window.removeEventListener('keydown', keyListenerBound);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
const keyListenerBound = keyListener.bind(this);
|
||||
window.addEventListener('keydown', keyListenerBound);
|
||||
}
|
||||
|
||||
async show() {
|
||||
document.body.append(this.dom);
|
||||
this.dom.style.display = 'block';
|
||||
switch(this.type) {
|
||||
case POPUP_TYPE.INPUT: {
|
||||
this.input.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$(this.dom).transition({
|
||||
opacity: 1,
|
||||
duration: animation_duration,
|
||||
easing: animation_easing,
|
||||
});
|
||||
|
||||
this.promise = new Promise((resolve) => {
|
||||
this.resolver = resolve;
|
||||
});
|
||||
return this.promise;
|
||||
}
|
||||
|
||||
completeAffirmative() {
|
||||
switch (this.type) {
|
||||
case POPUP_TYPE.TEXT:
|
||||
case POPUP_TYPE.CONFIRM: {
|
||||
this.value = true;
|
||||
break;
|
||||
}
|
||||
case POPUP_TYPE.INPUT: {
|
||||
this.value = this.input.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.result = POPUP_RESULT.AFFIRMATIVE;
|
||||
this.hide();
|
||||
}
|
||||
|
||||
completeNegative() {
|
||||
switch (this.type) {
|
||||
case POPUP_TYPE.TEXT:
|
||||
case POPUP_TYPE.CONFIRM:
|
||||
case POPUP_TYPE.INPUT: {
|
||||
this.value = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.result = POPUP_RESULT.NEGATIVE;
|
||||
this.hide();
|
||||
}
|
||||
|
||||
completeCancelled() {
|
||||
switch (this.type) {
|
||||
case POPUP_TYPE.TEXT:
|
||||
case POPUP_TYPE.CONFIRM:
|
||||
case POPUP_TYPE.INPUT: {
|
||||
this.value = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.result = POPUP_RESULT.CANCELLED;
|
||||
this.hide();
|
||||
}
|
||||
|
||||
|
||||
|
||||
hide() {
|
||||
$(this.dom).transition({
|
||||
opacity: 0,
|
||||
duration: animation_duration,
|
||||
easing: animation_easing,
|
||||
});
|
||||
delay(animation_duration).then(()=>{
|
||||
this.dom.remove();
|
||||
});
|
||||
|
||||
this.resolver(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Displays a blocking popup with a given text and type.
|
||||
* @param {JQuery<HTMLElement>|string|Element} text - Text to display in the popup.
|
||||
* @param {POPUP_TYPE} type
|
||||
* @param {string} inputValue - Value to set the input to.
|
||||
* @param {PopupOptions} options - Options for the popup.
|
||||
* @returns
|
||||
*/
|
||||
export function callGenericPopup(text, type, inputValue = '', { okButton, cancelButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling } = {}) {
|
||||
const popup = new Popup(
|
||||
text,
|
||||
type,
|
||||
inputValue,
|
||||
{ okButton, rows, wide, large, allowHorizontalScrolling, allowVerticalScrolling },
|
||||
);
|
||||
return popup.show();
|
||||
}
|
@ -49,6 +49,7 @@
|
||||
<li><tt>{{maxPrompt}}</tt> – max allowed prompt length in tokens = (context size - response length)</li>
|
||||
<li><tt>{{exampleSeparator}}</tt> – context template example dialogues separator</li>
|
||||
<li><tt>{{chatStart}}</tt> – context template chat start line</li>
|
||||
<li><tt>{{systemPrompt}}</tt> – main system prompt (either character prompt override if chosen, or instructSystemPrompt)</li>
|
||||
<li><tt>{{instructSystemPrompt}}</tt> – instruct system prompt</li>
|
||||
<li><tt>{{instructSystemPromptPrefix}}</tt> – instruct system prompt prefix sequence</li>
|
||||
<li><tt>{{instructSystemPromptSuffix}}</tt> – instruct system prompt suffix sequence</li>
|
||||
|
@ -2136,7 +2136,8 @@ grammarly-extension {
|
||||
/* Focus */
|
||||
|
||||
#bulk_tag_popup,
|
||||
#dialogue_popup {
|
||||
#dialogue_popup,
|
||||
.dialogue_popup {
|
||||
width: 500px;
|
||||
max-width: 90vw;
|
||||
max-width: 90svw;
|
||||
@ -2189,7 +2190,8 @@ grammarly-extension {
|
||||
}
|
||||
|
||||
#bulk_tag_popup_holder,
|
||||
#dialogue_popup_holder {
|
||||
#dialogue_popup_holder,
|
||||
.dialogue_popup_holder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
@ -2197,13 +2199,15 @@ grammarly-extension {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
#dialogue_popup_text {
|
||||
#dialogue_popup_text,
|
||||
.dialogue_popup_text {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#dialogue_popup_controls {
|
||||
#dialogue_popup_controls,
|
||||
.dialogue_popup_controls {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
gap: 20px;
|
||||
@ -2211,14 +2215,16 @@ grammarly-extension {
|
||||
|
||||
#bulk_tag_popup_reset,
|
||||
#bulk_tag_popup_remove_mutual,
|
||||
#dialogue_popup_ok {
|
||||
#dialogue_popup_ok,
|
||||
.dialogue_popup_ok {
|
||||
background-color: var(--crimson70a);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#bulk_tag_popup_reset:hover,
|
||||
#bulk_tag_popup_remove_mutual:hover,
|
||||
#dialogue_popup_ok:hover {
|
||||
#dialogue_popup_ok:hover,
|
||||
.dialogue_popup_ok:hover {
|
||||
background-color: var(--crimson-hover);
|
||||
}
|
||||
|
||||
@ -2226,13 +2232,15 @@ grammarly-extension {
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
#dialogue_popup_input {
|
||||
#dialogue_popup_input,
|
||||
.dialogue_popup_input {
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#bulk_tag_popup_cancel,
|
||||
#dialogue_popup_cancel {
|
||||
#dialogue_popup_cancel,
|
||||
.dialogue_popup_cancel {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -2297,7 +2305,7 @@ grammarly-extension {
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
#shadow_popup {
|
||||
#shadow_popup, .shadow_popup {
|
||||
backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||
-webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
|
||||
background-color: var(--black30a);
|
||||
@ -2309,6 +2317,9 @@ grammarly-extension {
|
||||
height: 100svh;
|
||||
z-index: 9999;
|
||||
top: 0;
|
||||
&.shadow_popup {
|
||||
z-index: 9998;
|
||||
}
|
||||
}
|
||||
|
||||
#bgtest {
|
||||
|
@ -341,6 +341,7 @@ redirect('/savequickreply', '/api/quick-replies/save');
|
||||
// Redirect deprecated image endpoints
|
||||
redirect('/uploadimage', '/api/images/upload');
|
||||
redirect('/listimgfiles/:folder', '/api/images/list/:folder');
|
||||
redirect('/api/content/import', '/api/content/importURL');
|
||||
|
||||
// Redirect deprecated moving UI endpoints
|
||||
redirect('/savemovingui', '/api/moving-ui/save');
|
||||
|
@ -74,6 +74,8 @@ router.post('/create', jsonParser, (request, response) => {
|
||||
chat_id: request.body.chat_id ?? id,
|
||||
chats: request.body.chats ?? [id],
|
||||
auto_mode_delay: request.body.auto_mode_delay ?? 5,
|
||||
generation_mode_join_prefix: request.body.generation_mode_join_prefix ?? '',
|
||||
generation_mode_join_suffix: request.body.generation_mode_join_suffix ?? '',
|
||||
};
|
||||
const pathToFile = path.join(DIRECTORIES.groups, `${id}.json`);
|
||||
const fileData = JSON.stringify(groupMetadata);
|
||||
|
Reference in New Issue
Block a user