2023-08-24 22:52:03 +02:00
import { humanizedDateTime , favsToHotswap , getMessageTimeStamp , dragElement , isMobile , initRossMods , } from "./scripts/RossAscends-mods.js" ;
2023-07-20 19:32:15 +02:00
import { userStatsHandler , statMesProcess } from './scripts/stats.js' ;
import {
generateKoboldWithStreaming ,
kai _settings ,
loadKoboldSettings ,
formatKoboldUrl ,
getKoboldGenerationData ,
2023-09-01 00:07:04 +02:00
kai _flags ,
setKoboldFlags ,
2023-07-20 19:32:15 +02:00
} from "./scripts/kai-settings.js" ;
import {
textgenerationwebui _settings ,
loadTextGenSettings ,
generateTextGenWithStreaming ,
getTextGenGenerationData ,
2023-08-03 12:07:54 +02:00
formatTextGenURL ,
2023-07-20 19:32:15 +02:00
} from "./scripts/textgen-settings.js" ;
import {
world _info ,
getWorldInfoPrompt ,
2023-08-10 19:45:57 +02:00
getWorldInfoSettings ,
2023-07-20 19:32:15 +02:00
setWorldInfoSettings ,
world _names ,
importEmbeddedWorldInfo ,
checkEmbeddedWorld ,
setWorldInfoButtonClass ,
importWorldInfo ,
} from "./scripts/world-info.js" ;
import {
groups ,
selected _group ,
saveGroupChat ,
getGroups ,
generateGroupWrapper ,
deleteGroup ,
is _group _generating ,
resetSelectedGroup ,
select _group _chats ,
regenerateGroup ,
group _generation _id ,
getGroupChat ,
renameGroupMember ,
createNewGroupChat ,
getGroupPastChats ,
getGroupAvatar ,
openGroupChat ,
editGroup ,
deleteGroupChat ,
renameGroupChat ,
importGroupChat ,
2023-08-18 22:13:15 +02:00
getGroupBlock ,
2023-08-19 18:45:20 +02:00
getGroupChatNames ,
2023-07-20 19:32:15 +02:00
} from "./scripts/group-chats.js" ;
import {
collapseNewlines ,
loadPowerUserSettings ,
playMessageSound ,
fixMarkdown ,
power _user ,
persona _description _positions ,
loadMovingUIState ,
getCustomStoppingStrings ,
2023-07-23 22:52:31 +02:00
MAX _CONTEXT _DEFAULT ,
2023-08-17 21:47:34 +02:00
renderStoryString ,
2023-08-18 22:13:15 +02:00
sortEntitiesList ,
2023-08-27 22:20:43 +02:00
registerDebugFunction ,
2023-08-29 17:04:10 +02:00
ui _mode ,
switchSimpleMode ,
2023-07-20 19:32:15 +02:00
} from "./scripts/power-user.js" ;
import {
setOpenAIMessageExamples ,
setOpenAIMessages ,
2023-07-24 17:30:31 +02:00
setupChatCompletionPromptManager ,
2023-07-20 19:32:15 +02:00
prepareOpenAIMessages ,
sendOpenAIRequest ,
loadOpenAISettings ,
setOpenAIOnlineStatus ,
generateOpenAIPromptCache ,
oai _settings ,
is _get _status _openai ,
openai _messages _count ,
chat _completion _sources ,
getChatCompletionModel ,
} from "./scripts/openai.js" ;
import {
generateNovelWithStreaming ,
getNovelGenerationData ,
2023-08-12 13:21:05 +02:00
getKayraMaxContextTokens ,
2023-07-20 19:32:15 +02:00
getNovelTier ,
loadNovelPreset ,
loadNovelSettings ,
nai _settings ,
2023-08-17 16:40:38 +02:00
adjustNovelInstructionPrompt ,
2023-09-04 17:00:15 +02:00
loadNovelSubscriptionData ,
2023-07-20 19:32:15 +02:00
} from "./scripts/nai-settings.js" ;
import {
createNewBookmark ,
showBookmarksButtons
} from "./scripts/bookmarks.js" ;
import {
horde _settings ,
loadHordeSettings ,
generateHorde ,
checkHordeStatus ,
getHordeModels ,
adjustHordeGenerationParams ,
MIN _AMOUNT _GEN ,
} from "./scripts/horde.js" ;
import {
debounce ,
delay ,
restoreCaretPosition ,
saveCaretPosition ,
2023-09-09 16:31:27 +02:00
trimToEndSentence ,
2023-07-20 19:32:15 +02:00
countOccurrences ,
isOdd ,
sortMoments ,
timestampToMoment ,
download ,
isDataURL ,
getCharaFilename ,
2023-08-08 21:36:42 +02:00
isDigitsOnly ,
2023-08-21 20:10:11 +02:00
PAGINATION _TEMPLATE ,
2023-08-23 20:32:38 +02:00
waitUntilCondition ,
2023-08-25 12:46:30 +02:00
escapeRegex ,
2023-08-25 21:04:06 +02:00
resetScrollHeight ,
2023-09-06 13:19:29 +02:00
onlyUnique ,
2023-07-20 19:32:15 +02:00
} from "./scripts/utils.js" ;
2023-08-22 21:45:12 +02:00
import { extension _settings , getContext , loadExtensionSettings , processExtensionHelpers , registerExtensionHelper , runGenerationInterceptors , saveMetadataDebounced } from "./scripts/extensions.js" ;
2023-08-26 19:26:23 +02:00
import { COMMENT _NAME _DEFAULT , executeSlashCommands , getSlashCommandsHelp , registerSlashCommand } from "./scripts/slash-commands.js" ;
2023-07-20 19:32:15 +02:00
import {
tag _map ,
tags ,
loadTagsSettings ,
printTagFilters ,
getTagsList ,
appendTagToList ,
createTagMapFromList ,
renameTagKey ,
importTags ,
tag _filter _types ,
} from "./scripts/tags.js" ;
import {
SECRET _KEYS ,
readSecretState ,
secret _state ,
writeSecret
} from "./scripts/secrets.js" ;
2023-08-19 15:29:46 +02:00
import { EventEmitter } from './lib/eventemitter.js' ;
2023-07-20 19:32:15 +02:00
import { markdownExclusionExt } from "./scripts/showdown-exclusion.js" ;
2023-08-25 15:46:54 +02:00
import { NOTE _MODULE _NAME , initAuthorsNote , metadata _keys , setFloatingPrompt , shouldWIAddPrompt } from "./scripts/authors-note.js" ;
2023-08-24 22:52:03 +02:00
import { getDeviceInfo } from "./scripts/RossAscends-mods.js" ;
2023-08-01 18:21:27 +02:00
import { registerPromptManagerMigration } from "./scripts/PromptManager.js" ;
2023-07-20 19:32:15 +02:00
import { getRegexedString , regex _placement } from "./scripts/extensions/regex/engine.js" ;
2023-08-18 22:13:15 +02:00
import { FILTER _TYPES , FilterHelper } from "./scripts/filters.js" ;
2023-08-20 06:44:39 +02:00
import { getCfgPrompt , getGuidanceScale } from "./scripts/extensions/cfg/util.js" ;
2023-08-20 22:29:43 +02:00
import {
2023-08-25 22:02:11 +02:00
force _output _sequence ,
2023-08-20 22:29:43 +02:00
formatInstructModeChat ,
formatInstructModePrompt ,
2023-08-20 22:33:26 +02:00
formatInstructModeExamples ,
2023-08-20 22:29:43 +02:00
getInstructStoppingSequences ,
autoSelectInstructPreset ,
2023-08-23 17:04:22 +02:00
formatInstructModeSystemPrompt ,
2023-08-20 22:29:43 +02:00
} from "./scripts/instruct-mode.js" ;
2023-08-22 09:37:18 +02:00
import { applyLocale } from "./scripts/i18n.js" ;
2023-08-23 01:38:43 +02:00
import { getTokenCount , getTokenizerModel , saveTokenCache } from "./scripts/tokenizers.js" ;
2023-08-29 00:54:11 +02:00
import { initPersonas , selectCurrentPersona , setPersonaDescription } from "./scripts/personas.js" ;
2023-09-10 03:08:15 +02:00
import { loadMancerModels } from "./scripts/mancer-settings.js" ;
2023-07-20 19:32:15 +02:00
//exporting functions and vars for mods
export {
Generate ,
getSettings ,
saveSettings ,
saveSettingsDebounced ,
printMessages ,
clearChat ,
getChat ,
getCharacters ,
callPopup ,
substituteParams ,
sendSystemMessage ,
addOneMessage ,
deleteLastMessage ,
resetChatState ,
select _rm _info ,
setCharacterId ,
setCharacterName ,
replaceCurrentChat ,
setOnlineStatus ,
checkOnlineStatus ,
setEditedMessageId ,
setSendButtonState ,
selectRightMenuWithAnimation ,
openCharacterChat ,
saveChat ,
messageFormatting ,
getExtensionPrompt ,
showSwipeButtons ,
hideSwipeButtons ,
changeMainAPI ,
setGenerationProgress ,
updateChatMetadata ,
scrollChatToBottom ,
isStreamingEnabled ,
getThumbnailUrl ,
getStoppingStrings ,
getStatus ,
reloadMarkdownProcessor ,
getCurrentChatId ,
chat ,
this _chid ,
selected _button ,
menu _type ,
settings ,
characters ,
online _status ,
main _api ,
api _server ,
system _messages ,
nai _settings ,
token ,
name1 ,
name2 ,
is _send _press ,
api _server _textgenerationwebui ,
max _context ,
chat _metadata ,
streamingProcessor ,
default _avatar ,
system _message _types ,
talkativeness _default ,
default _ch _mes ,
extension _prompt _types ,
mesForShowdownParse ,
printCharacters ,
}
2023-08-22 09:37:18 +02:00
// Allow target="_blank" in links
DOMPurify . addHook ( 'afterSanitizeAttributes' , function ( node ) {
if ( 'target' in node ) {
node . setAttribute ( 'target' , '_blank' ) ;
node . setAttribute ( 'rel' , 'noopener' ) ;
}
} ) ;
2023-07-20 19:32:15 +02:00
// API OBJECT FOR EXTERNAL WIRING
window [ "SillyTavern" ] = { } ;
// Event source init
export const event _types = {
EXTRAS _CONNECTED : 'extras_connected' ,
MESSAGE _SWIPED : 'message_swiped' ,
MESSAGE _SENT : 'message_sent' ,
MESSAGE _RECEIVED : 'message_received' ,
MESSAGE _EDITED : 'message_edited' ,
MESSAGE _DELETED : 'message_deleted' ,
IMPERSONATE _READY : 'impersonate_ready' ,
CHAT _CHANGED : 'chat_id_changed' ,
GENERATION _STOPPED : 'generation_stopped' ,
EXTENSIONS _FIRST _LOAD : 'extensions_first_load' ,
SETTINGS _LOADED : 'settings_loaded' ,
SETTINGS _UPDATED : 'settings_updated' ,
GROUP _UPDATED : 'group_updated' ,
MOVABLE _PANELS _RESET : 'movable_panels_reset' ,
2023-07-06 21:29:40 +02:00
SETTINGS _LOADED _BEFORE : 'settings_loaded_before' ,
SETTINGS _LOADED _AFTER : 'settings_loaded_after' ,
2023-08-05 18:23:28 +02:00
CHATCOMPLETION _SOURCE _CHANGED : 'chatcompletion_source_changed' ,
CHATCOMPLETION _MODEL _CHANGED : 'chatcompletion_model_changed' ,
2023-07-06 21:29:40 +02:00
OAI _BEFORE _CHATCOMPLETION : 'oai_before_chatcompletion' ,
2023-09-01 22:23:03 +02:00
OAI _PRESET _CHANGED _BEFORE : 'oai_preset_changed_before' ,
OAI _PRESET _CHANGED _AFTER : 'oai_preset_changed_after' ,
2023-07-06 21:29:40 +02:00
WORLDINFO _SETTINGS _UPDATED : 'worldinfo_settings_updated' ,
2023-07-07 22:45:53 +02:00
CHARACTER _EDITED : 'character_edited' ,
2023-08-22 21:45:12 +02:00
USER _MESSAGE _RENDERED : 'user_message_rendered' ,
CHARACTER _MESSAGE _RENDERED : 'character_message_rendered' ,
2023-09-09 21:15:47 +02:00
FORCE _SET _BACKGROUND : 'force_set_background' ,
CHAT _DELETED : 'chat_deleted' ,
GROUP _CHAT _DELETED : 'group_chat_deleted' ,
2023-07-20 19:32:15 +02:00
}
export const eventSource = new EventEmitter ( ) ;
2023-08-22 21:45:12 +02:00
// Check for override warnings every 5 seconds...
setInterval ( displayOverrideWarnings , 5000 ) ;
// ...or when the chat changes
2023-08-29 17:04:10 +02:00
eventSource . on ( event _types . SETTINGS _LOADED , ( ) => { settingsReady = true ; } ) ;
2023-08-22 21:45:12 +02:00
eventSource . on ( event _types . CHAT _CHANGED , displayOverrideWarnings ) ;
eventSource . on ( event _types . MESSAGE _RECEIVED , processExtensionHelpers ) ;
eventSource . on ( event _types . MESSAGE _SENT , processExtensionHelpers ) ;
2023-07-20 19:32:15 +02:00
hljs . addPlugin ( { "before:highlightElement" : ( { el } ) => { el . textContent = el . innerText } } ) ;
// Markdown converter
let mesForShowdownParse ; //intended to be used as a context to compare showdown strings against
let converter ;
reloadMarkdownProcessor ( ) ;
// array for prompt token calculations
console . debug ( 'initializing Prompt Itemization Array on Startup' ) ;
let itemizedPrompts = [ ] ;
/* let bg_menu_toggle = false; */
export const systemUserName = "SillyTavern System" ;
2023-07-21 15:42:18 +02:00
let default _user _name = "User" ;
2023-07-20 19:32:15 +02:00
let name1 = default _user _name ;
let name2 = "SillyTavern System" ;
let chat = [ ] ;
let safetychat = [
{
name : systemUserName ,
is _user : false ,
is _name : true ,
create _date : 0 ,
mes : "You deleted a character/chat and arrived back here for safety reasons! Pick another character!" ,
} ,
] ;
2023-08-23 20:32:38 +02:00
let chatSaveTimeout ;
2023-08-27 03:01:13 +02:00
let importFlashTimeout ;
2023-08-23 20:32:38 +02:00
export let isChatSaving = false ;
2023-07-20 19:32:15 +02:00
let chat _create _date = 0 ;
2023-07-21 15:42:18 +02:00
let firstRun = false ;
2023-08-29 17:04:10 +02:00
let settingsReady = false ;
2023-07-20 19:32:15 +02:00
const default _ch _mes = "Hello" ;
let count _view _mes = 0 ;
2023-08-27 18:24:28 +02:00
let generatedPromptCache = "" ;
2023-07-20 19:32:15 +02:00
let generation _started = new Date ( ) ;
let characters = [ ] ;
let this _chid ;
2023-08-23 15:48:44 +02:00
let saveCharactersPage = 0 ;
2023-07-20 19:32:15 +02:00
let backgrounds = [ ] ;
const default _avatar = "img/ai4.png" ;
export const system _avatar = "img/five.png" ;
export const comment _avatar = "img/quill.png" ;
export let CLIENT _VERSION = 'SillyTavern:UNKNOWN:Cohee#1207' ; // For Horde header
let optionsPopper = Popper . createPopper ( document . getElementById ( 'options_button' ) , document . getElementById ( 'options' ) , {
placement : 'top-start'
} ) ;
let exportPopper = Popper . createPopper ( document . getElementById ( 'export_button' ) , document . getElementById ( 'export_format_popup' ) , {
placement : 'left'
} ) ;
let rawPromptPopper = Popper . createPopper ( document . getElementById ( 'dialogue_popup' ) , document . getElementById ( 'rawPromptPopup' ) , {
placement : 'right'
} ) ;
let dialogueResolve = null ;
let chat _metadata = { } ;
let streamingProcessor = null ;
let crop _data = undefined ;
let is _delete _mode = false ;
let fav _ch _checked = false ;
let scrollLock = false ;
const durationSaveEdit = 1000 ;
const saveSettingsDebounced = debounce ( ( ) => saveSettings ( ) , durationSaveEdit ) ;
export const saveCharacterDebounced = debounce ( ( ) => $ ( "#create_button" ) . trigger ( 'click' ) , durationSaveEdit ) ;
const system _message _types = {
HELP : "help" ,
WELCOME : "welcome" ,
GROUP : "group" ,
EMPTY : "empty" ,
GENERIC : "generic" ,
BOOKMARK _CREATED : "bookmark_created" ,
BOOKMARK _BACK : "bookmark_back" ,
NARRATOR : "narrator" ,
COMMENT : "comment" ,
SLASH _COMMANDS : "slash_commands" ,
FORMATTING : "formatting" ,
HOTKEYS : "hotkeys" ,
MACROS : "macros" ,
} ;
const extension _prompt _types = {
2023-09-08 00:26:26 +02:00
IN _PROMPT : 0 ,
2023-07-20 19:32:15 +02:00
IN _CHAT : 1
} ;
2023-08-22 09:37:18 +02:00
let system _messages = { } ;
function getSystemMessages ( ) {
system _messages = {
help : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : renderTemplate ( "help" ) ,
} ,
slash _commands : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : '' ,
} ,
hotkeys : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : renderTemplate ( "hotkeys" ) ,
} ,
formatting : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : renderTemplate ( "formatting" ) ,
} ,
macros : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : renderTemplate ( "macros" ) ,
} ,
welcome :
{
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : renderTemplate ( "welcome" ) ,
} ,
group : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
is _group : true ,
mes : "Group chat created. Say 'Hi' to lovely people!" ,
} ,
empty : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : "No one hears you. <b>Hint:</b> add more members to the group!" ,
} ,
generic : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : "Generic system message. User `text` parameter to override the contents" ,
} ,
bookmark _created : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : ` Bookmark created! Click here to open the bookmark chat: <a class="bookmark_link" file_name="{0}" href="javascript:void(null);">{1}</a> ` ,
} ,
bookmark _back : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : ` Click here to return to the previous chat: <a class="bookmark_link" file_name="{0}" href="javascript:void(null);">Return</a> ` ,
} ,
} ;
}
2023-07-20 19:32:15 +02:00
2023-06-29 19:26:35 +02:00
// Register configuration migrations
2023-08-10 19:56:18 +02:00
registerPromptManagerMigration ( ) ;
2023-06-29 19:26:35 +02:00
2023-07-20 19:32:15 +02:00
$ ( document ) . ajaxError ( function myErrorHandler ( _ , xhr ) {
if ( xhr . status == 403 ) {
2023-08-06 15:42:15 +02:00
toastr . warning (
"doubleCsrf errors in console are NORMAL in this case. If you want to run ST in multiple tabs, start the server with --disableCsrf option." ,
"Looks like you've opened SillyTavern in another browser tab" ,
{ timeOut : 0 , extendedTimeOut : 0 , preventDuplicates : true } ,
) ;
2023-07-20 19:32:15 +02:00
}
} ) ;
2023-08-22 09:37:18 +02:00
function getUrlSync ( url , cache = true ) {
return $ . ajax ( {
type : "GET" ,
url : url ,
cache : cache ,
async : false
} ) . responseText ;
}
2023-08-26 13:07:41 +02:00
const templateCache = { } ;
2023-08-25 19:34:26 +02:00
export function renderTemplate ( templateId , templateData = { } , sanitize = true , localize = true , fullPath = false ) {
2023-08-22 09:37:18 +02:00
try {
2023-08-25 19:34:26 +02:00
const pathToTemplate = fullPath ? templateId : ` /scripts/templates/ ${ templateId } .html ` ;
2023-08-26 13:07:41 +02:00
const templateContent = ( pathToTemplate in templateCache ) ? templateCache [ pathToTemplate ] : getUrlSync ( pathToTemplate ) ;
templateCache [ pathToTemplate ] = templateContent ;
2023-08-22 09:37:18 +02:00
const template = Handlebars . compile ( templateContent ) ;
let result = template ( templateData ) ;
if ( sanitize ) {
result = DOMPurify . sanitize ( result ) ;
}
if ( localize ) {
result = applyLocale ( result ) ;
}
return result ;
} catch ( err ) {
console . error ( "Error rendering template" , templateId , templateData , err ) ;
toastr . error ( "Check the DevTools console for more information." , "Error rendering template" ) ;
}
}
2023-07-20 19:32:15 +02:00
async function getClientVersion ( ) {
try {
const response = await fetch ( '/version' ) ;
const data = await response . json ( ) ;
CLIENT _VERSION = data . agent ;
let displayVersion = ` SillyTavern ${ data . pkgVersion } ` ;
if ( data . gitRevision && data . gitBranch ) {
2023-08-21 23:40:39 +02:00
displayVersion += ` ' ${ data . gitBranch } ' ( ${ data . gitRevision } ) ` ;
2023-07-20 19:32:15 +02:00
}
$ ( '#version_display' ) . text ( displayVersion ) ;
$ ( '#version_display_welcome' ) . text ( displayVersion ) ;
} catch ( err ) {
console . error ( "Couldn't get client version" , err ) ;
}
}
function reloadMarkdownProcessor ( render _formulas = false ) {
if ( render _formulas ) {
converter = new showdown . Converter ( {
2023-08-22 12:07:24 +02:00
emoji : true ,
underline : true ,
tables : true ,
parseImgDimensions : true ,
2023-07-20 19:32:15 +02:00
extensions : [
showdownKatex (
{
delimiters : [
{ left : '$$' , right : '$$' , display : true , asciimath : false } ,
{ left : '$' , right : '$' , display : false , asciimath : true } ,
]
}
) ] ,
} ) ;
}
else {
converter = new showdown . Converter ( {
2023-08-22 12:07:24 +02:00
emoji : true ,
literalMidWordUnderscores : true ,
parseImgDimensions : true ,
tables : true ,
2023-07-20 19:32:15 +02:00
} ) ;
}
// Inject the dinkus extension after creating the converter
// Maybe move this into power_user init?
setTimeout ( ( ) => {
if ( power _user ) {
converter . addExtension ( markdownExclusionExt ( ) , 'exclusion' ) ;
}
} , 1 )
return converter ;
}
function getCurrentChatId ( ) {
if ( selected _group ) {
return groups . find ( x => x . id == selected _group ) ? . chat _id ;
}
else if ( this _chid ) {
return characters [ this _chid ] . chat ;
}
}
const talkativeness _default = 0.5 ;
var is _advanced _char _open = false ;
var menu _type = "" ; //what is selected in the menu
var selected _button = "" ; //which button pressed
//create pole save
let create _save = {
name : "" ,
description : "" ,
creator _notes : "" ,
post _history _instructions : "" ,
character _version : "" ,
system _prompt : "" ,
tags : "" ,
creator : "" ,
personality : "" ,
first _message : "" ,
avatar : "" ,
scenario : "" ,
mes _example : "" ,
world : "" ,
talkativeness : talkativeness _default ,
alternate _greetings : [ ]
} ;
//animation right menu
2023-08-19 20:13:37 +02:00
let animation _duration = 125 ;
2023-07-20 19:32:15 +02:00
let animation _easing = "ease-in-out" ;
let popup _type = "" ;
let bg _file _for _del = "" ;
let chat _file _for _del = "" ;
let online _status = "no_connection" ;
let api _server = "" ;
let api _server _textgenerationwebui = "" ;
//var interval_timer = setInterval(getStatus, 2000);
2023-09-04 17:00:15 +02:00
//let interval_timer_novel = setInterval(getStatusNovel, 90000);
2023-07-20 19:32:15 +02:00
let is _get _status = false ;
let is _get _status _novel = false ;
let is _api _button _press = false ;
let is _api _button _press _novel = false ;
2023-08-03 05:25:24 +02:00
let api _use _mancer _webui = false ;
2023-07-20 19:32:15 +02:00
let is _send _press = false ; //Send generation
let this _del _mes = 0 ;
//message editing and chat scroll posistion persistence
var this _edit _mes _text = "" ;
var this _edit _mes _chname = "" ;
var this _edit _mes _id ;
var scroll _holder = 0 ;
var is _use _scroll _holder = false ;
//settings
var settings ;
2023-07-23 22:52:31 +02:00
export let koboldai _settings ;
export let koboldai _setting _names ;
2023-07-20 19:32:15 +02:00
var preset _settings = "gui" ;
2023-08-29 00:54:11 +02:00
export let user _avatar = "you.png" ;
2023-07-23 22:52:31 +02:00
export var amount _gen = 80 ; //default max length of AI generated responses
2023-07-20 19:32:15 +02:00
var max _context = 2048 ;
var tokens _already _generated = 0 ;
var message _already _generated = "" ;
var cycle _count _generation = 0 ;
var swipes = true ;
let extension _prompts = { } ;
var main _api ; // = "kobold";
//novel settings
2023-07-23 22:52:31 +02:00
export let novelai _settings ;
export let novelai _setting _names ;
2023-07-20 19:32:15 +02:00
let abortController ;
//css
var css _mes _bg = $ ( '<div class="mes"></div>' ) . css ( "background" ) ;
var css _send _form _display = $ ( "<div id=send_form></div>" ) . css ( "display" ) ;
let generate _loop _counter = 0 ;
const MAX _GENERATION _LOOPS = 5 ;
var kobold _horde _model = "" ;
let token ;
var PromptArrayItemForRawPromptDisplay ;
2023-08-18 22:13:15 +02:00
export let active _character = "" ;
export let active _group = "" ;
export const entitiesFilter = new FilterHelper ( debounce ( printCharacters , 100 ) ) ;
2023-07-30 01:48:08 +02:00
2023-07-20 19:32:15 +02:00
export function getRequestHeaders ( ) {
return {
"Content-Type" : "application/json" ,
"X-CSRF-Token" : token ,
} ;
}
$ . ajaxPrefilter ( ( options , originalOptions , xhr ) => {
xhr . setRequestHeader ( "X-CSRF-Token" , token ) ;
} ) ;
2023-08-31 19:31:12 +02:00
async function firstLoadInit ( ) {
try {
const tokenResponse = await fetch ( '/csrf-token' ) ;
const tokenData = await tokenResponse . json ( ) ;
token = tokenData . token ;
} catch {
toastr . error ( "Couldn't get CSRF token. Please refresh the page." , "Error" , { timeOut : 0 , extendedTimeOut : 0 , preventDuplicates : true } ) ;
throw new Error ( "Initialization failed" ) ;
}
2023-08-22 09:37:18 +02:00
getSystemMessages ( ) ;
2023-07-20 19:32:15 +02:00
sendSystemMessage ( system _message _types . WELCOME ) ;
await readSecretState ( ) ;
await getClientVersion ( ) ;
await getSettings ( "def" ) ;
await getUserAvatars ( ) ;
await getCharacters ( ) ;
await getBackgrounds ( ) ;
2023-08-31 19:31:12 +02:00
initAuthorsNote ( ) ;
initPersonas ( ) ;
initRossMods ( ) ;
}
2023-07-20 19:32:15 +02:00
function checkOnlineStatus ( ) {
///////// REMOVED LINES THAT DUPLICATE RA_CHeckOnlineStatus FEATURES
if ( online _status == "no_connection" ) {
$ ( "#online_status_indicator2" ) . css ( "background-color" , "red" ) ; //Kobold
$ ( "#online_status_text2" ) . html ( "No connection..." ) ;
$ ( "#online_status_indicator_horde" ) . css ( "background-color" , "red" ) ; //Kobold Horde
$ ( "#online_status_text_horde" ) . html ( "No connection..." ) ;
$ ( "#online_status_indicator3" ) . css ( "background-color" , "red" ) ; //Novel
$ ( "#online_status_text3" ) . html ( "No connection..." ) ;
$ ( ".online_status_indicator4" ) . css ( "background-color" , "red" ) ; //OAI / ooba
$ ( ".online_status_text4" ) . html ( "No connection..." ) ;
is _get _status = false ;
is _get _status _novel = false ;
setOpenAIOnlineStatus ( false ) ;
} else {
$ ( "#online_status_indicator2" ) . css ( "background-color" , "green" ) ; //kobold
$ ( "#online_status_text2" ) . html ( online _status ) ;
$ ( "#online_status_indicator_horde" ) . css ( "background-color" , "green" ) ; //Kobold Horde
$ ( "#online_status_text_horde" ) . html ( online _status ) ;
$ ( "#online_status_indicator3" ) . css ( "background-color" , "green" ) ; //novel
$ ( "#online_status_text3" ) . html ( online _status ) ;
$ ( ".online_status_indicator4" ) . css ( "background-color" , "green" ) ; //OAI / ooba
$ ( ".online_status_text4" ) . html ( online _status ) ;
}
}
2023-07-30 01:48:08 +02:00
export function setActiveCharacter ( character ) {
active _character = character ;
}
export function setActiveGroup ( group ) {
active _group = group ;
2023-07-30 18:08:24 +02:00
}
2023-07-30 01:48:08 +02:00
2023-07-20 19:32:15 +02:00
async function getStatus ( ) {
if ( is _get _status ) {
if ( main _api == "koboldhorde" ) {
try {
const hordeStatus = await checkHordeStatus ( ) ;
online _status = hordeStatus ? 'Connected' : 'no_connection' ;
resultCheckStatus ( ) ;
}
catch {
online _status = "no_connection" ;
resultCheckStatus ( ) ;
}
return ;
}
jQuery . ajax ( {
type : "POST" , //
url : "/getstatus" , //
data : JSON . stringify ( {
2023-08-03 05:25:24 +02:00
api _server : main _api == "kobold" ? api _server : api _server _textgenerationwebui ,
2023-07-20 19:32:15 +02:00
main _api : main _api ,
2023-08-03 05:25:24 +02:00
use _mancer : main _api == "textgenerationwebui" ? api _use _mancer _webui : false ,
2023-07-20 19:32:15 +02:00
} ) ,
beforeSend : function ( ) { } ,
cache : false ,
dataType : "json" ,
crossDomain : true ,
contentType : "application/json" ,
//processData: false,
success : function ( data ) {
online _status = data . result ;
if ( online _status == undefined ) {
online _status = "no_connection" ;
}
2023-08-20 22:29:43 +02:00
// Determine instruct mode preset
autoSelectInstructPreset ( online _status ) ;
2023-07-20 19:32:15 +02:00
// determine if we can use stop sequence and streaming
if ( main _api === "kobold" || main _api === "koboldhorde" ) {
2023-09-01 00:07:04 +02:00
setKoboldFlags ( data . version , data . koboldVersion ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-03 13:32:53 +02:00
// We didn't get a 200 status code, but the endpoint has an explanation. Which means it DID connect, but I digress.
if ( online _status == "no_connection" && data . response ) {
2023-08-05 14:44:15 +02:00
toastr . error ( data . response , "API Error" , { timeOut : 5000 , preventDuplicates : true } )
2023-08-03 13:32:53 +02:00
}
2023-07-20 19:32:15 +02:00
//console.log(online_status);
resultCheckStatus ( ) ;
} ,
error : function ( jqXHR , exception ) {
console . log ( exception ) ;
console . log ( jqXHR ) ;
online _status = "no_connection" ;
resultCheckStatus ( ) ;
} ,
} ) ;
} else {
if ( is _get _status _novel != true && is _get _status _openai != true ) {
online _status = "no_connection" ;
}
}
}
function resultCheckStatus ( ) {
is _api _button _press = false ;
checkOnlineStatus ( ) ;
$ ( "#api_loading" ) . css ( "display" , "none" ) ;
$ ( "#api_button" ) . css ( "display" , "inline-block" ) ;
$ ( "#api_loading_textgenerationwebui" ) . css ( "display" , "none" ) ;
$ ( "#api_button_textgenerationwebui" ) . css ( "display" , "inline-block" ) ;
}
2023-08-18 22:13:15 +02:00
export function selectCharacterById ( id ) {
if ( characters [ id ] == undefined ) {
return ;
}
2023-07-20 19:32:15 +02:00
2023-08-23 20:32:38 +02:00
if ( isChatSaving ) {
toastr . info ( "Please wait until the chat is saved before switching characters." , "Your chat is still saving..." ) ;
return ;
}
2023-08-18 22:13:15 +02:00
if ( selected _group && is _group _generating ) {
return ;
}
2023-07-20 19:32:15 +02:00
2023-08-18 22:13:15 +02:00
if ( selected _group || this _chid !== id ) {
//if clicked on a different character from what was currently selected
if ( ! is _send _press ) {
cancelTtsPlay ( ) ;
resetSelectedGroup ( ) ;
this _edit _mes _id = undefined ;
selected _button = "character_edit" ;
this _chid = id ;
clearChat ( ) ;
chat . length = 0 ;
chat _metadata = { } ;
getChat ( ) ;
2023-07-27 23:04:21 +02:00
}
2023-08-18 22:13:15 +02:00
} else {
//if clicked on character that was already selected
selected _button = "character_edit" ;
select _selected _character ( this _chid ) ;
}
}
2023-07-27 23:04:21 +02:00
2023-08-18 22:13:15 +02:00
function getCharacterBlock ( item , id ) {
let this _avatar = default _avatar ;
if ( item . avatar != "none" ) {
this _avatar = getThumbnailUrl ( 'avatar' , item . avatar ) ;
}
// Populate the template
const template = $ ( '#character_template .character_select' ) . clone ( ) ;
template . attr ( { 'chid' : id , 'id' : ` CharID ${ id } ` } ) ;
template . find ( 'img' ) . attr ( 'src' , this _avatar ) ;
template . find ( '.avatar' ) . attr ( 'title' , item . avatar ) ;
2023-08-20 18:09:19 +02:00
template . find ( '.ch_name' ) . text ( item . name ) ;
2023-08-18 22:13:15 +02:00
if ( power _user . show _card _avatar _urls ) {
template . find ( '.ch_avatar_url' ) . text ( item . avatar ) ;
}
template . find ( '.ch_fav_icon' ) . css ( "display" , 'none' ) ;
template . toggleClass ( 'is_fav' , item . fav || item . fav == 'true' ) ;
template . find ( '.ch_fav' ) . val ( item . fav ) ;
const description = item . data ? . creator _notes ? . split ( '\n' , 1 ) [ 0 ] || '' ;
if ( description ) {
template . find ( '.ch_description' ) . text ( description ) ;
}
else {
template . find ( '.ch_description' ) . hide ( ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-18 22:13:15 +02:00
const version = item . data ? . character _version || '' ;
if ( version ) {
template . find ( '.character_version' ) . text ( version ) ;
}
else {
template . find ( '.character_version' ) . hide ( ) ;
}
// Display inline tags
const tags = getTagsList ( item . avatar ) ;
const tagsElement = template . find ( '.tags' ) ;
tags . forEach ( tag => appendTagToList ( tagsElement , tag , { } ) ) ;
// Add to the list
return template ;
}
async function printCharacters ( fullRefresh = false ) {
2023-08-27 01:13:46 +02:00
if ( fullRefresh ) {
saveCharactersPage = 0 ;
printTagFilters ( tag _filter _types . character ) ;
printTagFilters ( tag _filter _types . group _member ) ;
await delay ( 1 ) ;
displayOverrideWarnings ( ) ;
}
2023-08-18 22:13:15 +02:00
const storageKey = 'Characters_PerPage' ;
$ ( "#rm_print_characters_pagination" ) . pagination ( {
dataSource : getEntitiesList ( { doFilter : true } ) ,
pageSize : Number ( localStorage . getItem ( storageKey ) ) || 50 ,
2023-08-19 15:11:09 +02:00
sizeChangerOptions : [ 10 , 25 , 50 , 100 , 250 , 500 , 1000 ] ,
2023-08-18 22:13:15 +02:00
pageRange : 1 ,
2023-08-23 15:48:44 +02:00
pageNumber : saveCharactersPage || 1 ,
2023-08-18 22:13:15 +02:00
position : 'top' ,
showPageNumbers : false ,
showSizeChanger : true ,
prevText : '<' ,
nextText : '>' ,
2023-08-21 20:10:11 +02:00
formatNavigator : PAGINATION _TEMPLATE ,
2023-08-18 22:13:15 +02:00
showNavigator : true ,
callback : function ( data ) {
$ ( "#rm_print_characters_block" ) . empty ( ) ;
for ( const i of data ) {
if ( i . type === 'character' ) {
$ ( "#rm_print_characters_block" ) . append ( getCharacterBlock ( i . item , i . id ) ) ;
}
if ( i . type === 'group' ) {
$ ( "#rm_print_characters_block" ) . append ( getGroupBlock ( i . item ) ) ;
}
}
} ,
afterSizeSelectorChange : function ( e ) {
localStorage . setItem ( storageKey , e . target . value ) ;
2023-08-23 15:48:44 +02:00
} ,
afterPaging : function ( e ) {
saveCharactersPage = e ;
} ,
2023-08-27 01:13:46 +02:00
afterRender : function ( ) {
$ ( '#rm_print_characters_block' ) . scrollTop ( 0 ) ;
} ,
2023-07-20 19:32:15 +02:00
} ) ;
favsToHotswap ( ) ;
2023-08-18 22:13:15 +02:00
}
export function getEntitiesList ( { doFilter } = { } ) {
let entities = [ ] ;
entities . push ( ... characters . map ( ( item , index ) => ( { item , id : index , type : 'character' } ) ) ) ;
entities . push ( ... groups . map ( ( item ) => ( { item , id : item . id , type : 'group' } ) ) ) ;
if ( doFilter ) {
entities = entitiesFilter . applyFilters ( entities ) ;
}
sortEntitiesList ( entities ) ;
return entities ;
2023-07-20 19:32:15 +02:00
}
2023-08-19 14:58:17 +02:00
async function getOneCharacter ( avatarUrl ) {
const response = await fetch ( "/getonecharacter" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
avatar _url : avatarUrl ,
} ) ,
} ) ;
if ( response . ok ) {
const getData = await response . json ( ) ;
getData [ 'name' ] = DOMPurify . sanitize ( getData [ 'name' ] ) ;
getData [ 'chat' ] = String ( getData [ 'chat' ] ) ;
const indexOf = characters . findIndex ( x => x . avatar === avatarUrl ) ;
if ( indexOf !== - 1 ) {
characters [ indexOf ] = getData ;
} else {
toastr . error ( ` Character ${ avatarUrl } not found in the list ` , "Error" , { timeOut : 5000 , preventDuplicates : true } ) ;
}
}
}
2023-07-20 19:32:15 +02:00
async function getCharacters ( ) {
var response = await fetch ( "/getcharacters" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
"" : "" ,
} ) ,
} ) ;
if ( response . ok === true ) {
var getData = "" ; //RossAscends: reset to force array to update to account for deleted character.
getData = await response . json ( ) ;
const load _ch _count = Object . getOwnPropertyNames ( getData ) ;
for ( var i = 0 ; i < load _ch _count . length ; i ++ ) {
characters [ i ] = [ ] ;
characters [ i ] = getData [ i ] ;
characters [ i ] [ 'name' ] = DOMPurify . sanitize ( characters [ i ] [ 'name' ] ) ;
// For dropped-in cards
if ( ! characters [ i ] [ 'chat' ] ) {
characters [ i ] [ 'chat' ] = ` ${ characters [ i ] [ 'name' ] } - ${ humanizedDateTime ( ) } ` ;
}
characters [ i ] [ 'chat' ] = String ( characters [ i ] [ 'chat' ] ) ;
}
if ( this _chid != undefined && this _chid != "invalid-safety-id" ) {
$ ( "#avatar_url_pole" ) . val ( characters [ this _chid ] . avatar ) ;
}
await getGroups ( ) ;
2023-08-18 22:13:15 +02:00
await printCharacters ( true ) ;
2023-07-20 19:32:15 +02:00
}
}
async function getBackgrounds ( ) {
const response = await fetch ( "/getbackgrounds" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
"" : "" ,
} ) ,
} ) ;
if ( response . ok === true ) {
const getData = await response . json ( ) ;
//background = getData;
//console.log(getData.length);
2023-07-20 22:11:37 +02:00
$ ( "#bg_menu_content" ) . children ( 'div' ) . remove ( ) ;
2023-07-20 19:32:15 +02:00
for ( const bg of getData ) {
const template = getBackgroundFromTemplate ( bg ) ;
$ ( "#bg_menu_content" ) . append ( template ) ;
}
}
}
function getBackgroundFromTemplate ( bg ) {
const thumbPath = getThumbnailUrl ( 'bg' , bg ) ;
const template = $ ( '#background_template .bg_example' ) . clone ( ) ;
template . attr ( 'bgfile' , bg ) ;
template . attr ( 'title' , bg ) ;
template . find ( '.bg_button' ) . attr ( 'bgfile' , bg ) ;
template . css ( 'background-image' , ` url(' ${ thumbPath } ') ` ) ;
template . find ( '.BGSampleTitle' ) . text ( bg . slice ( 0 , bg . lastIndexOf ( '.' ) ) ) ;
return template ;
}
async function setBackground ( bg ) {
jQuery . ajax ( {
type : "POST" , //
url : "/setbackground" , //
data : JSON . stringify ( {
bg : bg ,
} ) ,
beforeSend : function ( ) {
} ,
cache : false ,
dataType : "json" ,
contentType : "application/json" ,
//processData: false,
success : function ( html ) { } ,
error : function ( jqXHR , exception ) {
console . log ( exception ) ;
console . log ( jqXHR ) ;
} ,
} ) ;
}
async function delBackground ( bg ) {
const response = await fetch ( "/delbackground" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
bg : bg ,
} ) ,
} ) ;
if ( response . ok === true ) {
}
}
async function delChat ( chatfile ) {
const response = await fetch ( "/delchat" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
chatfile : chatfile ,
avatar _url : characters [ this _chid ] . avatar ,
} ) ,
} ) ;
if ( response . ok === true ) {
// choose another chat if current was deleted
2023-09-09 21:15:47 +02:00
const name = chatfile . replace ( '.jsonl' , '' ) ;
if ( name === characters [ this _chid ] . chat ) {
2023-07-20 19:32:15 +02:00
chat _metadata = { } ;
await replaceCurrentChat ( ) ;
}
2023-09-09 21:15:47 +02:00
await eventSource . emit ( event _types . CHAT _DELETED , name ) ;
2023-07-20 19:32:15 +02:00
}
}
async function replaceCurrentChat ( ) {
clearChat ( ) ;
chat . length = 0 ;
const chatsResponse = await fetch ( "/getallchatsofcharacter" , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { avatar _url : characters [ this _chid ] . avatar } )
} ) ;
if ( chatsResponse . ok ) {
const chats = Object . values ( await chatsResponse . json ( ) ) ;
// pick existing chat
if ( chats . length && typeof chats [ 0 ] === 'object' ) {
characters [ this _chid ] . chat = chats [ 0 ] . file _name . replace ( '.jsonl' , '' ) ;
$ ( "#selected_chat_pole" ) . val ( characters [ this _chid ] . chat ) ;
saveCharacterDebounced ( ) ;
await getChat ( ) ;
}
// start new chat
else {
characters [ this _chid ] . chat = name2 + " - " + humanizedDateTime ( ) ;
$ ( "#selected_chat_pole" ) . val ( characters [ this _chid ] . chat ) ;
saveCharacterDebounced ( ) ;
await getChat ( ) ;
}
}
}
2023-09-11 00:07:45 +02:00
const TRUNCATION _THRESHOLD = 100 ;
function showMoreMessages ( ) {
let messageId = Number ( $ ( '#chat' ) . children ( '.mes' ) . first ( ) . attr ( 'mesid' ) ) ;
let count = TRUNCATION _THRESHOLD ;
console . debug ( 'Inserting messages before' , messageId , 'count' , count , 'chat length' , chat . length ) ;
const prevHeight = $ ( '#chat' ) . prop ( 'scrollHeight' ) ;
while ( messageId > 0 && count > 0 ) {
count -- ;
messageId -- ;
addOneMessage ( chat [ messageId ] , { insertBefore : messageId + 1 , scroll : false , forceId : messageId } ) ;
}
if ( messageId == 0 ) {
$ ( '#show_more_messages' ) . remove ( ) ;
}
const newHeight = $ ( '#chat' ) . prop ( 'scrollHeight' ) ;
$ ( '#chat' ) . scrollTop ( newHeight - prevHeight ) ;
}
2023-09-08 11:10:41 +02:00
async function printMessages ( ) {
2023-09-11 00:07:45 +02:00
let startIndex = 0 ;
if ( chat . length > TRUNCATION _THRESHOLD ) {
count _view _mes = chat . length - TRUNCATION _THRESHOLD ;
startIndex = count _view _mes ;
$ ( '#chat' ) . append ( '<div id="show_more_messages">Show more messages</div>' ) ;
}
for ( let i = startIndex ; i < chat . length ; i ++ ) {
2023-09-08 11:10:41 +02:00
const item = chat [ i ] ;
addOneMessage ( item , { scroll : i === chat . length - 1 } ) ;
}
2023-08-12 12:10:41 +02:00
if ( power _user . lazy _load > 0 ) {
const height = $ ( '#chat' ) . height ( ) ;
const scrollHeight = $ ( '#chat' ) . prop ( 'scrollHeight' ) ;
// Only hide if oveflowing the scroll
if ( scrollHeight > height ) {
$ ( '#chat' ) . children ( '.mes' ) . slice ( 0 , - power _user . lazy _load ) . hide ( ) ;
}
}
2023-07-20 19:32:15 +02:00
}
function clearChat ( ) {
count _view _mes = 0 ;
extension _prompts = { } ;
$ ( "#chat" ) . children ( ) . remove ( ) ;
if ( $ ( '.zoomed_avatar[forChar]' ) . length ) {
console . debug ( 'saw avatars to remove' )
$ ( '.zoomed_avatar[forChar]' ) . remove ( ) ;
} else { console . debug ( 'saw no avatars' ) }
itemizedPrompts = [ ] ;
}
async function deleteLastMessage ( ) {
count _view _mes -- ;
chat . length = chat . length - 1 ;
$ ( '#chat' ) . children ( '.mes' ) . last ( ) . remove ( ) ;
await eventSource . emit ( event _types . MESSAGE _DELETED , chat . length ) ;
}
export async function reloadCurrentChat ( ) {
clearChat ( ) ;
chat . length = 0 ;
if ( selected _group ) {
await getGroupChat ( selected _group ) ;
}
else if ( this _chid ) {
await getChat ( ) ;
}
else {
resetChatState ( ) ;
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-07-21 13:15:43 +02:00
await eventSource . emit ( event _types . CHAT _CHANGED , getCurrentChatId ( ) ) ;
2023-07-20 19:32:15 +02:00
}
function messageFormatting ( mes , ch _name , isSystem , isUser ) {
if ( mes ) {
mesForShowdownParse = mes ;
}
if ( ! mes ) {
mes = '' ;
}
2023-08-26 19:26:23 +02:00
// Force isSystem = false on comment messages so they get formatted properly
if ( ch _name === COMMENT _NAME _DEFAULT && isSystem && ! isUser ) {
isSystem = false ;
}
2023-07-20 19:32:15 +02:00
// Prompt bias replacement should be applied on the raw message
if ( ! power _user . show _user _prompt _bias && ch _name && ! isUser && ! isSystem ) {
mes = mes . replaceAll ( substituteParams ( power _user . user _prompt _bias ) , "" ) ;
}
if ( ! isSystem ) {
let regexPlacement ;
if ( isUser ) {
regexPlacement = regex _placement . USER _INPUT ;
} else if ( ch _name !== name2 ) {
regexPlacement = regex _placement . SLASH _COMMAND ;
} else {
regexPlacement = regex _placement . AI _OUTPUT ;
}
// Always override the character name
mes = getRegexedString ( mes , regexPlacement , {
characterOverride : ch _name ,
isMarkdown : true
} ) ;
}
if ( power _user . auto _fix _generated _markdown ) {
2023-08-31 13:39:31 +02:00
mes = fixMarkdown ( mes , true ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-10 10:47:17 +02:00
if ( ! isSystem && power _user . encode _tags ) {
mes = mes . replaceAll ( "<" , "<" ) . replaceAll ( ">" , ">" ) ;
}
2023-07-20 19:32:15 +02:00
if ( ( this _chid === undefined || this _chid === "invalid-safety-id" ) && ! selected _group ) {
mes = mes
. replace ( /\*\*(.+?)\*\*/g , "<b>$1</b>" )
. replace ( /\n/g , "<br/>" ) ;
} else if ( ! isSystem ) {
mes = mes . replace ( /```[\s\S]*?```|``[\s\S]*?``|`[\s\S]*?`|(\".+?\")|(\u201C.+?\u201D)/gm , function ( match , p1 , p2 ) {
if ( p1 ) {
return '<q>"' + p1 . replace ( /\"/g , "" ) + '"</q>' ;
} else if ( p2 ) {
return '<q>“' + p2 . replace ( /\u201C|\u201D/g , "" ) + '”</q>' ;
} else {
return match ;
}
} ) ;
mes = mes . replaceAll ( '\\begin{align*}' , '$$' ) ;
mes = mes . replaceAll ( '\\end{align*}' , '$$' ) ;
mes = converter . makeHtml ( mes ) ;
mes = replaceBiasMarkup ( mes ) ;
mes = mes . replace ( /<code(.*)>[\s\S]*?<\/code>/g , function ( match ) {
// Firefox creates extra newlines from <br>s in code blocks, so we replace them before converting newlines to <br>s.
return match . replace ( /\n/gm , '\u0000' ) ;
} )
mes = mes . replace ( /\n/g , "<br/>" ) ;
mes = mes . replace ( /\u0000/g , "\n" ) ; // Restore converted newlines
mes = mes . trim ( ) ;
mes = mes . replace ( /<code(.*)>[\s\S]*?<\/code>/g , function ( match ) {
return match . replace ( /&/g , '&' ) ;
} ) ;
}
/ *
// Hides bias from empty messages send with slash commands
if ( isSystem ) {
mes = mes . replace ( /\{\{[\s\S]*?\}\}/gm , "" ) ;
}
* /
if ( ! power _user . allow _name2 _display && ch _name && ! isUser && ! isSystem ) {
2023-08-06 23:31:26 +02:00
mes = mes . replace ( new RegExp ( ` (^| \n ) ${ ch _name } : ` , 'g' ) , "$1" ) ;
2023-07-20 19:32:15 +02:00
}
2023-07-24 23:07:52 +02:00
mes = DOMPurify . sanitize ( mes ) ;
2023-07-26 20:08:25 +02:00
2023-07-20 19:32:15 +02:00
return mes ;
}
2023-08-14 00:43:16 +02:00
/ * *
2023-08-14 04:57:49 +02:00
* Inserts or replaces an SVG icon adjacent to the provided message ' s timestamp .
2023-08-14 00:43:16 +02:00
*
* If the ` extra.api ` is "openai" and ` extra.model ` contains the substring "claude" ,
* the function fetches the "claude.svg" . Otherwise , it fetches the SVG named after
* the value in ` extra.api ` .
*
2023-08-31 16:10:01 +02:00
* @ param { JQuery < HTMLElement > } mes - The message element containing the timestamp where the icon should be inserted or replaced .
2023-08-14 00:43:16 +02:00
* @ param { Object } extra - Contains the API and model details .
* @ param { string } extra . api - The name of the API , used to determine which SVG to fetch .
* @ param { string } extra . model - The model name , used to check for the substring "claude" .
* /
function insertSVGIcon ( mes , extra ) {
// Determine the SVG filename
let modelName ;
2023-08-15 20:36:28 +02:00
// Claude on OpenRouter or Anthropic
if ( extra . api === "openai" && extra . model ? . toLowerCase ( ) . includes ( "claude" ) ) {
2023-08-14 00:43:16 +02:00
modelName = "claude" ;
2023-08-15 20:36:28 +02:00
}
// OpenAI on OpenRouter
else if ( extra . api === "openai" && extra . model ? . toLowerCase ( ) . includes ( "openai" ) ) {
modelName = "openai" ;
}
// OpenRouter website model or other models
else if ( extra . api === "openai" && ( extra . model === null || extra . model ? . toLowerCase ( ) . includes ( "/" ) ) ) {
modelName = "openrouter" ;
}
// Everything else
else {
2023-08-14 00:43:16 +02:00
modelName = extra . api ;
}
2023-08-19 18:48:33 +02:00
const image = new Image ( ) ;
// Add classes for styling and identification
image . classList . add ( 'icon-svg' , 'timestamp-icon' ) ;
image . src = ` /img/ ${ modelName } .svg ` ;
2023-08-27 12:13:55 +02:00
image . title = ` ${ extra ? . api ? extra . api + ' - ' : '' } ${ extra ? . model ? ? '' } ` ;
2023-08-14 00:43:16 +02:00
2023-08-19 18:48:33 +02:00
image . onload = async function ( ) {
2023-08-14 04:57:49 +02:00
// Check if an SVG already exists adjacent to the timestamp
let existingSVG = mes . find ( '.timestamp' ) . next ( '.timestamp-icon' ) ;
if ( existingSVG . length ) {
// Replace existing SVG
2023-08-19 18:48:33 +02:00
existingSVG . replaceWith ( image ) ;
2023-08-14 04:57:49 +02:00
} else {
// Append the new SVG if none exists
2023-08-19 18:48:33 +02:00
mes . find ( '.timestamp' ) . after ( image ) ;
2023-08-14 04:57:49 +02:00
}
2023-08-19 18:48:33 +02:00
await SVGInject ( this ) ;
} ;
2023-08-14 00:43:16 +02:00
}
2023-08-14 04:57:49 +02:00
2023-07-20 19:32:15 +02:00
function getMessageFromTemplate ( {
mesId ,
characterName ,
isUser ,
avatarImg ,
bias ,
isSystem ,
title ,
timerValue ,
timerTitle ,
bookmarkLink ,
forceAvatar ,
timestamp ,
2023-08-31 16:10:01 +02:00
tokenCount ,
2023-07-20 19:32:15 +02:00
extra ,
} = { } ) {
const mes = $ ( '#message_template .mes' ) . clone ( ) ;
mes . attr ( {
'mesid' : mesId ,
'ch_name' : characterName ,
'is_user' : isUser ,
'is_system' : ! ! isSystem ,
'bookmark_link' : bookmarkLink ,
'force_avatar' : ! ! forceAvatar ,
'timestamp' : timestamp ,
} ) ;
mes . find ( '.avatar img' ) . attr ( 'src' , avatarImg ) ;
mes . find ( '.ch_name .name_text' ) . text ( characterName ) ;
mes . find ( '.mes_bias' ) . html ( bias ) ;
mes . find ( '.timestamp' ) . text ( timestamp ) . attr ( 'title' , ` ${ extra ? . api ? extra . api + ' - ' : '' } ${ extra ? . model ? ? '' } ` ) ;
mes . find ( '.mesIDDisplay' ) . text ( ` # ${ mesId } ` ) ;
2023-08-31 16:10:01 +02:00
tokenCount && mes . find ( '.tokenCounterDisplay' ) . text ( ` ${ tokenCount } t ` ) ;
2023-07-20 19:32:15 +02:00
title && mes . attr ( 'title' , title ) ;
timerValue && mes . find ( '.mes_timer' ) . attr ( 'title' , timerTitle ) . text ( timerValue ) ;
2023-08-14 00:43:16 +02:00
if ( power _user . timestamp _model _icon && extra ? . api ) {
insertSVGIcon ( mes , extra ) ;
}
2023-07-20 19:32:15 +02:00
return mes ;
}
export function updateMessageBlock ( messageId , message ) {
const messageElement = $ ( ` #chat [mesid=" ${ messageId } "] ` ) ;
const text = message ? . extra ? . display _text ? ? message . mes ;
messageElement . find ( '.mes_text' ) . html ( messageFormatting ( text , message . name , message . is _system , message . is _user ) ) ;
addCopyToCodeBlocks ( messageElement )
appendImageToMessage ( message , messageElement ) ;
}
export function appendImageToMessage ( mes , messageElement ) {
if ( mes . extra ? . image ) {
const image = messageElement . find ( '.mes_img' ) ;
const text = messageElement . find ( '.mes_text' ) ;
const isInline = ! ! mes . extra ? . inline _image ;
image . attr ( 'src' , mes . extra ? . image ) ;
image . attr ( 'title' , mes . extra ? . title || mes . title || '' ) ;
messageElement . find ( ".mes_img_container" ) . addClass ( "img_extra" ) ;
image . toggleClass ( "img_inline" , isInline ) ;
text . toggleClass ( 'displayNone' , ! isInline ) ;
}
}
export function addCopyToCodeBlocks ( messageElement ) {
const codeBlocks = $ ( messageElement ) . find ( "pre code" ) ;
for ( let i = 0 ; i < codeBlocks . length ; i ++ ) {
hljs . highlightElement ( codeBlocks . get ( i ) ) ;
if ( navigator . clipboard !== undefined ) {
const copyButton = document . createElement ( 'i' ) ;
copyButton . classList . add ( 'fa-solid' , 'fa-copy' , 'code-copy' ) ;
copyButton . title = 'Copy code' ;
codeBlocks . get ( i ) . appendChild ( copyButton ) ;
copyButton . addEventListener ( 'pointerup' , function ( event ) {
navigator . clipboard . writeText ( codeBlocks . get ( i ) . innerText ) ;
toastr . info ( 'Copied!' , '' , { timeOut : 2000 } ) ;
} ) ;
}
}
}
2023-09-11 00:07:45 +02:00
function addOneMessage ( mes , { type = "normal" , insertAfter = null , scroll = true , insertBefore = null , forceId = null } = { } ) {
2023-07-20 19:32:15 +02:00
var messageText = mes [ "mes" ] ;
const momentDate = timestampToMoment ( mes . send _date ) ;
const timestamp = momentDate . isValid ( ) ? momentDate . format ( 'LL LT' ) : '' ;
if ( mes ? . extra ? . display _text ) {
messageText = mes . extra . display _text ;
}
// Forbidden black magic
// This allows to use "continue" on user messages
if ( type === 'swipe' && mes . swipe _id === undefined ) {
mes . swipe _id = 0 ;
mes . swipes = [ mes . mes ] ;
}
if ( mes . name === name1 ) {
var characterName = name1 ; //set to user's name by default
} else { var characterName = mes . name }
var avatarImg = getUserAvatar ( user _avatar ) ;
const isSystem = mes . is _system ;
const title = mes . title ;
2023-08-27 18:24:28 +02:00
generatedPromptCache = "" ;
2023-07-20 19:32:15 +02:00
//for non-user mesages
if ( ! mes [ "is_user" ] ) {
if ( mes . force _avatar ) {
avatarImg = mes . force _avatar ;
} else if ( this _chid === undefined || this _chid === "invalid-safety-id" ) {
avatarImg = system _avatar ;
} else {
if ( characters [ this _chid ] . avatar != "none" ) {
avatarImg = getThumbnailUrl ( 'avatar' , characters [ this _chid ] . avatar ) ;
} else {
avatarImg = default _avatar ;
}
}
//old processing:
//if messge is from sytem, use the name provided in the message JSONL to proceed,
//if not system message, use name2 (char's name) to proceed
//characterName = mes.is_system || mes.force_avatar ? mes.name : name2;
} else if ( mes [ "is_user" ] && mes [ "force_avatar" ] ) {
// Special case for persona images.
avatarImg = mes [ "force_avatar" ] ;
}
if ( count _view _mes == 0 ) {
messageText = substituteParams ( messageText ) ;
}
messageText = messageFormatting (
messageText ,
characterName ,
isSystem ,
mes . is _user ,
) ;
const bias = messageFormatting ( mes . extra ? . bias ? ? "" ) ;
2023-08-19 18:45:20 +02:00
let bookmarkLink = mes ? . extra ? . bookmark _link ? ? '' ;
// Verify bookmarked chat still exists
2023-08-20 18:43:58 +02:00
// Cohee: Commented out for now. I'm worried of performance issues.
/ * i f ( b o o k m a r k L i n k ! = = ' ' ) {
2023-08-19 18:45:20 +02:00
let chat _names = selected _group
? getGroupChatNames ( selected _group )
2023-08-20 18:43:58 +02:00
: Object . values ( getPastCharacterChats ( ) ) . map ( ( { file _name } ) => file _name ) ;
2023-08-19 18:45:20 +02:00
if ( ! chat _names . includes ( bookmarkLink ) ) {
bookmarkLink = ''
}
2023-08-20 18:43:58 +02:00
} * /
2023-07-20 19:32:15 +02:00
let params = {
2023-09-11 00:07:45 +02:00
mesId : forceId ? ? count _view _mes ,
2023-07-20 19:32:15 +02:00
characterName : characterName ,
isUser : mes . is _user ,
avatarImg : avatarImg ,
bias : bias ,
isSystem : isSystem ,
title : title ,
bookmarkLink : bookmarkLink ,
forceAvatar : mes . force _avatar ,
timestamp : timestamp ,
extra : mes . extra ,
2023-08-31 16:10:01 +02:00
tokenCount : mes . extra ? . token _count ,
... formatGenerationTimer ( mes . gen _started , mes . gen _finished , mes . extra ? . token _count ) ,
2023-07-20 19:32:15 +02:00
} ;
const HTMLForEachMes = getMessageFromTemplate ( params ) ;
if ( type !== 'swipe' ) {
2023-09-11 00:07:45 +02:00
if ( ! insertAfter && ! insertBefore ) {
2023-07-20 19:32:15 +02:00
$ ( "#chat" ) . append ( HTMLForEachMes ) ;
}
2023-09-11 00:07:45 +02:00
else if ( insertAfter ) {
2023-07-20 19:32:15 +02:00
const target = $ ( "#chat" ) . find ( ` .mes[mesid=" ${ insertAfter } "] ` ) ;
$ ( HTMLForEachMes ) . insertAfter ( target ) ;
$ ( HTMLForEachMes ) . find ( '.swipe_left' ) . css ( 'display' , 'none' ) ;
$ ( HTMLForEachMes ) . find ( '.swipe_right' ) . css ( 'display' , 'none' ) ;
2023-09-11 00:07:45 +02:00
} else {
const target = $ ( "#chat" ) . find ( ` .mes[mesid=" ${ insertBefore } "] ` ) ;
$ ( HTMLForEachMes ) . insertBefore ( target ) ;
$ ( HTMLForEachMes ) . find ( '.swipe_left' ) . css ( 'display' , 'none' ) ;
$ ( HTMLForEachMes ) . find ( '.swipe_right' ) . css ( 'display' , 'none' ) ;
}
}
function getMessageId ( ) {
if ( typeof forceId == 'number' ) {
return forceId ;
2023-07-20 19:32:15 +02:00
}
2023-09-11 00:07:45 +02:00
return type == 'swipe' ? count _view _mes - 1 : count _view _mes ;
2023-07-20 19:32:15 +02:00
}
2023-09-11 00:07:45 +02:00
const newMessageId = getMessageId ( ) ;
2023-07-20 19:32:15 +02:00
const newMessage = $ ( ` #chat [mesid=" ${ newMessageId } "] ` ) ;
const isSmallSys = mes ? . extra ? . isSmallSys ;
newMessage . data ( "isSystem" , isSystem ) ;
if ( isSystem ) {
// newMessage.find(".mes_edit").hide();
newMessage . find ( ".mes_prompt" ) . hide ( ) ; //don't need prompt button for sys
}
if ( isSmallSys === true ) {
newMessage . addClass ( 'smallSysMes' ) ;
}
// don't need prompt button for user
if ( params . isUser === true ) {
newMessage . find ( ".mes_prompt" ) . hide ( ) ;
//console.log(`hiding prompt for user mesID ${params.mesId}`);
}
//shows or hides the Prompt display button
let mesIdToFind = type == 'swipe' ? params . mesId - 1 : params . mesId ; //Number(newMessage.attr('mesId'));
//if we have itemized messages, and the array isn't null..
if ( params . isUser === false && itemizedPrompts . length !== 0 && itemizedPrompts . length !== null ) {
// console.log('looking through itemized prompts...');
//console.log(`mesIdToFind = ${mesIdToFind} from ${params.avatarImg}`);
//console.log(`itemizedPrompts.length = ${itemizedPrompts.length}`)
//console.log(itemizedPrompts);
for ( var i = 0 ; i < itemizedPrompts . length ; i ++ ) {
//console.log(`itemized array item ${i} is MesID ${Number(itemizedPrompts[i].mesId)}, does it match ${Number(mesIdToFind)}?`);
if ( Number ( itemizedPrompts [ i ] . mesId ) === Number ( mesIdToFind ) ) {
newMessage . find ( ".mes_prompt" ) . show ( ) ;
//console.log(`showing button for mesID ${params.mesId} from ${params.characterName}`);
break ;
} / * else {
console . log ( ` no cache obj for mesID ${ mesIdToFind } , hiding this prompt button ` ) ;
newMessage . find ( ".mes_prompt" ) . hide ( ) ;
console . log ( itemizedPrompts ) ;
} * /
}
} else {
//console.log('itemizedprompt array empty null, or user, hiding this prompt buttons');
//$(".mes_prompt").hide();
newMessage . find ( ".mes_prompt" ) . hide ( ) ;
//console.log(itemizedPrompts);
}
newMessage . find ( '.avatar img' ) . on ( 'error' , function ( ) {
$ ( this ) . hide ( ) ;
$ ( this ) . parent ( ) . html ( ` <div class="missing-avatar fa-solid fa-user-slash"></div> ` ) ;
} ) ;
if ( type === 'swipe' ) {
2023-08-31 16:10:01 +02:00
const swipeMessage = $ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) ;
swipeMessage . find ( '.mes_text' ) . html ( '' ) ;
swipeMessage . find ( '.mes_text' ) . append ( messageText ) ;
appendImageToMessage ( mes , swipeMessage ) ;
swipeMessage . attr ( 'title' , title ) ;
swipeMessage . find ( '.timestamp' ) . text ( timestamp ) . attr ( 'title' , ` ${ params . extra . api } - ${ params . extra . model } ` ) ;
2023-08-14 04:57:49 +02:00
if ( power _user . timestamp _model _icon && params . extra ? . api ) {
2023-08-31 16:10:01 +02:00
insertSVGIcon ( swipeMessage , params . extra ) ;
2023-08-14 04:57:49 +02:00
}
2023-07-20 19:32:15 +02:00
if ( mes . swipe _id == mes . swipes . length - 1 ) {
2023-08-31 16:10:01 +02:00
swipeMessage . find ( '.mes_timer' ) . text ( params . timerValue ) ;
swipeMessage . find ( '.mes_timer' ) . attr ( 'title' , params . timerTitle ) ;
swipeMessage . find ( '.tokenCounterDisplay' ) . text ( ` ${ params . tokenCount } t ` ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-08-31 16:10:01 +02:00
swipeMessage . find ( '.mes_timer' ) . html ( '' ) ;
swipeMessage . find ( '.tokenCounterDisplay' ) . html ( '' ) ;
2023-07-20 19:32:15 +02:00
}
2023-09-11 00:07:45 +02:00
} else if ( typeof forceId == 'number' ) {
$ ( "#chat" ) . find ( ` [mesid=" ${ forceId } "] ` ) . find ( '.mes_text' ) . append ( messageText ) ;
appendImageToMessage ( mes , newMessage ) ;
hideSwipeButtons ( ) ;
showSwipeButtons ( ) ;
2023-07-20 19:32:15 +02:00
} else {
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes } "] ` ) . find ( '.mes_text' ) . append ( messageText ) ;
appendImageToMessage ( mes , newMessage ) ;
hideSwipeButtons ( ) ;
count _view _mes ++ ;
}
addCopyToCodeBlocks ( newMessage ) ;
// Don't scroll if not inserting last
2023-09-11 00:07:45 +02:00
if ( ! insertAfter && ! insertBefore && scroll ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat .mes' ) . last ( ) . addClass ( 'last_mes' ) ;
$ ( '#chat .mes' ) . eq ( - 2 ) . removeClass ( 'last_mes' ) ;
hideSwipeButtons ( ) ;
showSwipeButtons ( ) ;
scrollChatToBottom ( ) ;
}
}
function getUserAvatar ( avatarImg ) {
return ` User Avatars/ ${ avatarImg } ` ;
}
2023-08-31 16:10:01 +02:00
/ * *
* Formats the title for the generation timer .
* @ param { Date } gen _started Date when generation was started
* @ param { Date } gen _finished Date when generation was finished
* @ param { number } tokenCount Number of tokens generated ( 0 if not available )
* @ returns { Object } Object containing the formatted timer value and title
* @ example
* const { timerValue , timerTitle } = formatGenerationTimer ( gen _started , gen _finished , tokenCount ) ;
* console . log ( timerValue ) ; // 1.2s
* console . log ( timerTitle ) ; // Generation queued: 12:34:56 7 Jan 2021\nReply received: 12:34:57 7 Jan 2021\nTime to generate: 1.2 seconds\nToken rate: 5 t/s
* /
function formatGenerationTimer ( gen _started , gen _finished , tokenCount ) {
2023-07-20 19:32:15 +02:00
if ( ! gen _started || ! gen _finished ) {
return { } ;
}
const dateFormat = 'HH:mm:ss D MMM YYYY' ;
const start = moment ( gen _started ) ;
const finish = moment ( gen _finished ) ;
const seconds = finish . diff ( start , 'seconds' , true ) ;
const timerValue = ` ${ seconds . toFixed ( 1 ) } s ` ;
const timerTitle = [
` Generation queued: ${ start . format ( dateFormat ) } ` ,
` Reply received: ${ finish . format ( dateFormat ) } ` ,
` Time to generate: ${ seconds } seconds ` ,
2023-08-31 16:10:01 +02:00
tokenCount > 0 ? ` Token rate: ${ Number ( tokenCount / seconds ) . toFixed ( 1 ) } t/s ` : '' ,
2023-07-20 19:32:15 +02:00
] . join ( '\n' ) ;
return { timerValue , timerTitle } ;
}
function scrollChatToBottom ( ) {
if ( power _user . auto _scroll _chat _to _bottom ) {
const chatElement = $ ( "#chat" ) ;
let position = chatElement [ 0 ] . scrollHeight ;
if ( power _user . waifuMode ) {
const lastMessage = chatElement . find ( '.mes' ) . last ( ) ;
if ( lastMessage . length ) {
const lastMessagePosition = lastMessage . position ( ) . top ;
position = chatElement . scrollTop ( ) + lastMessagePosition ;
}
}
chatElement . scrollTop ( position ) ;
}
}
2023-08-26 00:32:39 +02:00
/ * *
* Substitutes { { macro } } parameters in a string .
* @ param { string } content - The string to substitute parameters in .
* @ param { * } _name1 - The name of the user . Uses global name1 if not provided .
* @ param { * } _name2 - The name of the character . Uses global name2 if not provided .
* @ param { * } _original - The original message for { { original } } substitution .
* @ param { * } _group - The group members list for { { group } } substitution .
* @ returns { string } The string with substituted parameters .
* /
2023-06-06 18:03:30 +02:00
function substituteParams ( content , _name1 , _name2 , _original , _group ) {
2023-07-20 19:32:15 +02:00
_name1 = _name1 ? ? name1 ;
_name2 = _name2 ? ? name2 ;
2023-06-06 18:03:30 +02:00
_group = _group ? ? name2 ;
2023-07-21 11:00:40 +02:00
2023-07-20 19:32:15 +02:00
if ( ! content ) {
return '' ;
}
// Replace {{original}} with the original message
// Note: only replace the first instance of {{original}}
// This will hopefully prevent the abuse
2023-07-22 13:10:21 +02:00
if ( typeof _original === 'string' ) {
content = content . replace ( /{{original}}/i , _original ) ;
}
2023-07-20 19:32:15 +02:00
content = content . replace ( /{{input}}/gi , $ ( '#send_textarea' ) . val ( ) ) ;
content = content . replace ( /{{user}}/gi , _name1 ) ;
content = content . replace ( /{{char}}/gi , _name2 ) ;
2023-06-06 18:03:30 +02:00
content = content . replace ( /{{charIfNotGroup}}/gi , _group ) ;
content = content . replace ( /{{group}}/gi , _group ) ;
2023-07-20 19:32:15 +02:00
content = content . replace ( /<USER>/gi , _name1 ) ;
content = content . replace ( /<BOT>/gi , _name2 ) ;
2023-06-06 18:03:30 +02:00
content = content . replace ( /<CHARIFNOTGROUP>/gi , _group ) ;
content = content . replace ( /<GROUP>/gi , _group ) ;
2023-07-20 19:32:15 +02:00
content = content . replace ( /{{time}}/gi , moment ( ) . format ( 'LT' ) ) ;
content = content . replace ( /{{date}}/gi , moment ( ) . format ( 'LL' ) ) ;
content = content . replace ( /{{idle_duration}}/gi , ( ) => getTimeSinceLastMessage ( ) ) ;
content = content . replace ( /{{time_UTC([-+]\d+)}}/gi , ( _ , offset ) => {
const utcOffset = parseInt ( offset , 10 ) ;
const utcTime = moment ( ) . utc ( ) . utcOffset ( utcOffset ) . format ( 'LT' ) ;
return utcTime ;
} ) ;
content = randomReplace ( content ) ;
content = diceRollReplace ( content ) ;
return content ;
}
function getTimeSinceLastMessage ( ) {
const now = moment ( ) ;
if ( Array . isArray ( chat ) && chat . length > 0 ) {
let lastMessage ;
let takeNext = false ;
for ( let i = chat . length - 1 ; i >= 0 ; i -- ) {
const message = chat [ i ] ;
if ( message . is _system ) {
continue ;
}
if ( message . is _user && takeNext ) {
lastMessage = message ;
break ;
}
takeNext = true ;
}
if ( lastMessage ? . send _date ) {
const lastMessageDate = timestampToMoment ( lastMessage . send _date ) ;
const duration = moment . duration ( now . diff ( lastMessageDate ) ) ;
return duration . humanize ( ) ;
}
}
return 'just now' ;
}
function randomReplace ( input , emptyListPlaceholder = '' ) {
2023-08-08 21:36:42 +02:00
const randomPattern = /{{random[ : ]([^}]+)}}/gi ;
2023-07-20 19:32:15 +02:00
return input . replace ( randomPattern , ( match , listString ) => {
const list = listString . split ( ',' ) . map ( item => item . trim ( ) ) . filter ( item => item . length > 0 ) ;
if ( list . length === 0 ) {
return emptyListPlaceholder ;
}
var rng = new Math . seedrandom ( 'added entropy.' , { entropy : true } ) ;
const randomIndex = Math . floor ( rng ( ) * list . length ) ;
//const randomIndex = Math.floor(Math.random() * list.length);
return list [ randomIndex ] ;
} ) ;
}
function diceRollReplace ( input , invalidRollPlaceholder = '' ) {
2023-08-08 21:39:34 +02:00
const rollPattern = /{{roll[ : ]([^}]+)}}/gi ;
2023-07-20 19:32:15 +02:00
2023-08-08 21:39:34 +02:00
return input . replace ( rollPattern , ( match , matchValue ) => {
2023-08-08 21:36:42 +02:00
let formula = matchValue . trim ( ) ;
if ( isDigitsOnly ( formula ) ) {
formula = ` 1d ${ formula } ` ;
}
2023-07-20 19:32:15 +02:00
const isValid = droll . validate ( formula ) ;
if ( ! isValid ) {
console . debug ( ` Invalid roll formula: ${ formula } ` ) ;
return invalidRollPlaceholder ;
}
const result = droll . roll ( formula ) ;
return new String ( result . total ) ;
} ) ;
}
2023-09-06 13:19:29 +02:00
function getStoppingStrings ( isImpersonate ) {
2023-07-20 19:32:15 +02:00
const charString = ` \n ${ name2 } : ` ;
const userString = ` \n ${ name1 } : ` ;
2023-09-06 13:07:00 +02:00
const result = isImpersonate ? [ charString ] : [ userString ] ;
2023-07-20 19:32:15 +02:00
result . push ( userString ) ;
// Add other group members as the stopping strings
if ( selected _group ) {
const group = groups . find ( x => x . id === selected _group ) ;
if ( group && Array . isArray ( group . members ) ) {
const names = group . members
. map ( x => characters . find ( y => y . avatar == x ) )
. filter ( x => x && x . name !== name2 )
. map ( x => ` \n ${ x . name } : ` ) ;
result . push ( ... names ) ;
}
}
2023-08-20 22:29:43 +02:00
result . push ( ... getInstructStoppingSequences ( ) ) ;
2023-07-20 19:32:15 +02:00
if ( power _user . custom _stopping _strings ) {
const customStoppingStrings = getCustomStoppingStrings ( ) ;
2023-08-28 23:47:35 +02:00
result . push ( ... customStoppingStrings ) ;
2023-07-20 19:32:15 +02:00
}
2023-09-06 13:19:29 +02:00
return result . filter ( onlyUnique ) ;
2023-07-20 19:32:15 +02:00
}
// Background prompt generation
export async function generateQuietPrompt ( quiet _prompt ) {
return await new Promise (
async function promptPromise ( resolve , reject ) {
try {
await Generate ( 'quiet' , { resolve , reject , quiet _prompt , force _name2 : true , } ) ;
}
catch {
reject ( ) ;
}
} ) ;
}
function processCommands ( message , type ) {
if ( type == "regenerate" || type == "swipe" || type == 'quiet' ) {
return null ;
}
const result = executeSlashCommands ( message ) ;
$ ( "#send_textarea" ) . val ( result . newText ) . trigger ( 'input' ) ;
// interrupt generation if the input was nothing but a command
if ( message . length > 0 && result . newText . length === 0 ) {
return true ;
}
return result . interrupt ;
}
function sendSystemMessage ( type , text , extra = { } ) {
const systemMessage = system _messages [ type ] ;
if ( ! systemMessage ) {
return ;
}
2023-08-22 17:13:03 +02:00
const newMessage = { ... systemMessage , send _date : getMessageTimeStamp ( ) } ;
2023-07-20 19:32:15 +02:00
if ( text ) {
newMessage . mes = text ;
}
if ( type == system _message _types . SLASH _COMMANDS ) {
newMessage . mes = getSlashCommandsHelp ( ) ;
}
if ( ! newMessage . extra ) {
newMessage . extra = { } ;
}
newMessage . extra = Object . assign ( newMessage . extra , extra ) ;
newMessage . extra . type = type ;
chat . push ( newMessage ) ;
addOneMessage ( newMessage ) ;
is _send _press = false ;
}
export function extractMessageBias ( message ) {
if ( ! message ) {
return null ;
}
2023-08-22 15:14:21 +02:00
try {
const biasHandlebars = Handlebars . create ( ) ;
const biasMatches = [ ] ;
biasHandlebars . registerHelper ( 'bias' , function ( text ) {
biasMatches . push ( text ) ;
return '' ;
} ) ;
const template = biasHandlebars . compile ( message ) ;
template ( { } ) ;
2023-07-20 19:32:15 +02:00
2023-08-22 15:14:21 +02:00
if ( biasMatches && biasMatches . length > 0 ) {
return ` ${ biasMatches . join ( " " ) } ` ;
2023-07-20 19:32:15 +02:00
}
2023-08-22 15:14:21 +02:00
return '' ;
} catch {
return '' ;
2023-07-20 19:32:15 +02:00
}
}
2023-08-30 15:31:53 +02:00
/ * *
* Removes impersonated group member lines from the group member messages .
* Doesn ' t do anything if group reply trimming is disabled .
* @ param { string } getMessage Group message
* @ returns Cleaned - up group message
* /
2023-07-20 19:32:15 +02:00
function cleanGroupMessage ( getMessage ) {
2023-08-30 15:31:53 +02:00
if ( power _user . disable _group _trimming ) {
return getMessage ;
}
2023-07-20 19:32:15 +02:00
const group = groups . find ( ( x ) => x . id == selected _group ) ;
if ( group && Array . isArray ( group . members ) && group . members ) {
for ( let member of group . members ) {
const character = characters . find ( x => x . avatar == member ) ;
if ( ! character ) {
continue ;
}
const name = character . name ;
// Skip current speaker.
if ( name === name2 ) {
continue ;
}
2023-08-25 12:46:30 +02:00
const regex = new RegExp ( ` (^| \n ) ${ escapeRegex ( name ) } : ` ) ;
const nameMatch = getMessage . match ( regex ) ;
if ( nameMatch ) {
2023-08-29 13:53:39 +02:00
getMessage = getMessage . substring ( 0 , nameMatch . index ) ;
2023-07-20 19:32:15 +02:00
}
}
}
return getMessage ;
}
2023-08-23 23:26:47 +02:00
function addPersonaDescriptionExtensionPrompt ( ) {
2023-07-20 19:32:15 +02:00
if ( ! power _user . persona _description ) {
2023-08-23 23:26:47 +02:00
return ;
2023-07-20 19:32:15 +02:00
}
2023-08-23 23:26:47 +02:00
const promptPositions = [ persona _description _positions . BOTTOM _AN , persona _description _positions . TOP _AN ] ;
if ( promptPositions . includes ( power _user . persona _description _position ) && shouldWIAddPrompt ) {
const originalAN = extension _prompts [ NOTE _MODULE _NAME ] . value
const ANWithDesc = power _user . persona _description _position === persona _description _positions . TOP _AN
? ` ${ power _user . persona _description } \n ${ originalAN } `
: ` ${ originalAN } \n ${ power _user . persona _description } ` ;
setExtensionPrompt ( NOTE _MODULE _NAME , ANWithDesc , chat _metadata [ metadata _keys . position ] , chat _metadata [ metadata _keys . depth ] ) ;
2023-07-20 19:32:15 +02:00
}
}
function getAllExtensionPrompts ( ) {
const value = Object
. values ( extension _prompts )
. filter ( x => x . value )
. map ( x => x . value . trim ( ) )
. join ( '\n' ) ;
return value . length ? substituteParams ( value ) : '' ;
}
function getExtensionPrompt ( position = 0 , depth = undefined , separator = "\n" ) {
let extension _prompt = Object . keys ( extension _prompts )
. sort ( )
. map ( ( x ) => extension _prompts [ x ] )
. filter ( x => x . position == position && x . value && ( depth === undefined || x . depth == depth ) )
. map ( x => x . value . trim ( ) )
. join ( separator ) ;
if ( extension _prompt . length && ! extension _prompt . startsWith ( separator ) ) {
extension _prompt = separator + extension _prompt ;
}
if ( extension _prompt . length && ! extension _prompt . endsWith ( separator ) ) {
extension _prompt = extension _prompt + separator ;
}
if ( extension _prompt . length ) {
extension _prompt = substituteParams ( extension _prompt ) ;
}
return extension _prompt ;
}
function baseChatReplace ( value , name1 , name2 ) {
if ( value !== undefined && value . length > 0 ) {
value = substituteParams ( value , name1 , name2 ) ;
if ( power _user . collapse _newlines ) {
value = collapseNewlines ( value ) ;
}
2023-08-19 20:36:22 +02:00
value = value . replace ( /\r/g , '' ) ;
2023-07-20 19:32:15 +02:00
}
return value ;
}
function isStreamingEnabled ( ) {
2023-08-19 17:20:42 +02:00
return ( ( main _api == 'openai' && oai _settings . stream _openai && oai _settings . chat _completion _source !== chat _completion _sources . SCALE && oai _settings . chat _completion _source !== chat _completion _sources . AI21 )
2023-09-01 00:07:04 +02:00
|| ( main _api == 'kobold' && kai _settings . streaming _kobold && kai _flags . can _use _streaming )
2023-07-20 19:32:15 +02:00
|| ( main _api == 'novel' && nai _settings . streaming _novel )
|| ( main _api == 'textgenerationwebui' && textgenerationwebui _settings . streaming ) )
&& ! isMultigenEnabled ( ) ; // Multigen has a quasi-streaming mode which breaks the real streaming
}
function showStopButton ( ) {
$ ( '#mes_stop' ) . css ( { 'display' : 'flex' } ) ;
}
function hideStopButton ( ) {
$ ( '#mes_stop' ) . css ( { 'display' : 'none' } ) ;
}
class StreamingProcessor {
showMessageButtons ( messageId ) {
if ( messageId == - 1 ) {
return ;
}
showStopButton ( ) ;
$ ( ` #chat .mes[mesid=" ${ messageId } "] .mes_buttons ` ) . css ( { 'display' : 'none' } ) ;
}
hideMessageButtons ( messageId ) {
if ( messageId == - 1 ) {
return ;
}
hideStopButton ( ) ;
$ ( ` #chat .mes[mesid=" ${ messageId } "] .mes_buttons ` ) . css ( { 'display' : 'flex' } ) ;
}
2023-08-22 16:46:37 +02:00
async onStartStreaming ( text ) {
2023-07-20 19:32:15 +02:00
let messageId = - 1 ;
if ( this . type == "impersonate" ) {
$ ( '#send_textarea' ) . val ( '' ) . trigger ( 'input' ) ;
}
else {
2023-08-22 16:46:37 +02:00
await saveReply ( this . type , text ) ;
2023-07-20 19:32:15 +02:00
messageId = count _view _mes - 1 ;
this . showMessageButtons ( messageId ) ;
}
hideSwipeButtons ( ) ;
scrollChatToBottom ( ) ;
return messageId ;
}
removePrefix ( text ) {
const name1Marker = ` ${ name1 } : ` ;
const name2Marker = ` ${ name2 } : ` ;
if ( text ) {
if ( text . startsWith ( name1Marker ) ) {
text = text . replace ( name1Marker , '' ) ;
}
if ( text . startsWith ( name2Marker ) ) {
text = text . replace ( name2Marker , '' ) ;
}
}
return text ;
}
onProgressStreaming ( messageId , text , isFinal ) {
const isImpersonate = this . type == "impersonate" ;
const isContinue = this . type == "continue" ;
text = this . removePrefix ( text ) ;
let processedText = cleanUpMessage ( text , isImpersonate , isContinue , ! isFinal ) ;
let result = extractNameFromMessage ( processedText , this . force _name2 , isImpersonate ) ;
let isName = result . this _mes _is _name ;
processedText = result . getMessage ;
// Predict unbalanced asterisks / quotes during streaming
const charsToBalance = [ '*' , '"' ] ;
for ( const char of charsToBalance ) {
if ( ! isFinal && isOdd ( countOccurrences ( processedText , char ) ) ) {
// Add character at the end to balance it
processedText = processedText . trimEnd ( ) + char ;
}
}
if ( isImpersonate ) {
$ ( '#send_textarea' ) . val ( processedText ) . trigger ( 'input' ) ;
}
else {
let currentTime = new Date ( ) ;
2023-08-31 16:10:01 +02:00
// Don't waste time calculating token count for streaming
let currentTokenCount = isFinal && power _user . message _token _count _enabled ? getTokenCount ( processedText , 0 ) : 0 ;
const timePassed = formatGenerationTimer ( this . timeStarted , currentTime , currentTokenCount ) ;
2023-07-20 19:32:15 +02:00
chat [ messageId ] [ 'is_name' ] = isName ;
chat [ messageId ] [ 'mes' ] = processedText ;
chat [ messageId ] [ 'gen_started' ] = this . timeStarted ;
chat [ messageId ] [ 'gen_finished' ] = currentTime ;
2023-08-31 16:10:01 +02:00
if ( currentTokenCount ) {
if ( ! chat [ messageId ] [ 'extra' ] ) {
chat [ messageId ] [ 'extra' ] = { } ;
}
chat [ messageId ] [ 'extra' ] [ 'token_count' ] = currentTokenCount ;
const tokenCounter = $ ( ` #chat .mes[mesid=" ${ messageId } "] .tokenCounterDisplay ` ) ;
tokenCounter . text ( ` ${ currentTokenCount } t ` ) ;
}
2023-08-25 00:27:27 +02:00
if ( ( this . type == 'swipe' || this . type === 'continue' ) && Array . isArray ( chat [ messageId ] [ 'swipes' ] ) ) {
2023-07-20 19:32:15 +02:00
chat [ messageId ] [ 'swipes' ] [ chat [ messageId ] [ 'swipe_id' ] ] = processedText ;
chat [ messageId ] [ 'swipe_info' ] [ chat [ messageId ] [ 'swipe_id' ] ] = { 'send_date' : chat [ messageId ] [ 'send_date' ] , 'gen_started' : chat [ messageId ] [ 'gen_started' ] , 'gen_finished' : chat [ messageId ] [ 'gen_finished' ] , 'extra' : JSON . parse ( JSON . stringify ( chat [ messageId ] [ 'extra' ] ) ) } ;
}
let formattedText = messageFormatting (
processedText ,
chat [ messageId ] . name ,
chat [ messageId ] . is _system ,
chat [ messageId ] . is _user ,
) ;
const mesText = $ ( ` #chat .mes[mesid=" ${ messageId } "] .mes_text ` ) ;
mesText . html ( formattedText ) ;
$ ( ` #chat .mes[mesid=" ${ messageId } "] .mes_timer ` ) . text ( timePassed . timerValue ) . attr ( 'title' , timePassed . timerTitle ) ;
this . setFirstSwipe ( messageId ) ;
}
if ( ! scrollLock ) {
scrollChatToBottom ( ) ;
}
}
2023-08-22 16:46:37 +02:00
async onFinishStreaming ( messageId , text ) {
2023-07-20 19:32:15 +02:00
this . hideMessageButtons ( this . messageId ) ;
this . onProgressStreaming ( messageId , text , true ) ;
addCopyToCodeBlocks ( $ ( ` #chat .mes[mesid=" ${ messageId } "] ` ) ) ;
2023-08-22 21:45:12 +02:00
if ( this . type !== 'impersonate' ) {
await eventSource . emit ( event _types . MESSAGE _RECEIVED , this . messageId ) ;
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , this . messageId ) ;
} else {
await eventSource . emit ( event _types . IMPERSONATE _READY , text ) ;
}
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
activateSendButtons ( ) ;
showSwipeButtons ( ) ;
setGenerationProgress ( 0 ) ;
2023-08-27 18:24:28 +02:00
generatedPromptCache = '' ;
2023-07-20 19:32:15 +02:00
//console.log("Generated text size:", text.length, text)
if ( power _user . auto _swipe ) {
function containsBlacklistedWords ( str , blacklist , threshold ) {
const regex = new RegExp ( ` \\ b( ${ blacklist . join ( '|' ) } ) \\ b ` , 'gi' ) ;
const matches = str . match ( regex ) || [ ] ;
return matches . length >= threshold ;
}
const generatedTextFiltered = ( text ) => {
if ( text ) {
if ( power _user . auto _swipe _minimum _length ) {
if ( text . length < power _user . auto _swipe _minimum _length && text . length !== 0 ) {
console . log ( "Generated text size too small" )
return true
}
}
if ( power _user . auto _swipe _blacklist _threshold ) {
if ( containsBlacklistedWords ( text , power _user . auto _swipe _blacklist , power _user . auto _swipe _blacklist _threshold ) ) {
console . log ( "Generated text has blacklisted words" )
return true
}
}
}
return false
}
if ( generatedTextFiltered ( text ) ) {
swipe _right ( )
return
}
}
playMessageSound ( ) ;
}
onErrorStreaming ( ) {
this . hideMessageButtons ( this . messageId ) ;
$ ( "#send_textarea" ) . removeAttr ( 'disabled' ) ;
is _send _press = false ;
activateSendButtons ( ) ;
setGenerationProgress ( 0 ) ;
showSwipeButtons ( ) ;
}
setFirstSwipe ( messageId ) {
if ( this . type !== 'swipe' && this . type !== 'impersonate' ) {
if ( Array . isArray ( chat [ messageId ] [ 'swipes' ] ) && chat [ messageId ] [ 'swipes' ] . length === 1 && chat [ messageId ] [ 'swipe_id' ] === 0 ) {
chat [ messageId ] [ 'swipes' ] [ 0 ] = chat [ messageId ] [ 'mes' ] ;
chat [ messageId ] [ 'swipe_info' ] [ 0 ] = { 'send_date' : chat [ messageId ] [ 'send_date' ] , 'gen_started' : chat [ messageId ] [ 'gen_started' ] , 'gen_finished' : chat [ messageId ] [ 'gen_finished' ] , 'extra' : JSON . parse ( JSON . stringify ( chat [ messageId ] [ 'extra' ] ) ) } ;
}
}
}
onStopStreaming ( ) {
this . onErrorStreaming ( ) ;
}
2023-08-22 16:46:37 +02:00
* nullStreamingGeneration ( ) {
2023-07-20 19:32:15 +02:00
throw new Error ( 'Generation function for streaming is not hooked up' ) ;
}
constructor ( type , force _name2 ) {
this . result = "" ;
this . messageId = - 1 ;
this . type = type ;
this . force _name2 = force _name2 ;
this . isStopped = false ;
this . isFinished = false ;
this . generator = this . nullStreamingGeneration ;
this . abortController = new AbortController ( ) ;
this . firstMessageText = '...' ;
this . timeStarted = new Date ( ) ;
}
async generate ( ) {
if ( this . messageId == - 1 ) {
2023-08-22 16:46:37 +02:00
this . messageId = await this . onStartStreaming ( this . firstMessageText ) ;
2023-07-20 19:32:15 +02:00
await delay ( 1 ) ; // delay for message to be rendered
scrollLock = false ;
}
try {
for await ( const text of this . generator ( ) ) {
if ( this . isStopped ) {
this . onStopStreaming ( ) ;
return ;
}
this . result = text ;
this . onProgressStreaming ( this . messageId , message _already _generated + text ) ;
}
}
catch ( err ) {
console . error ( err ) ;
this . onErrorStreaming ( ) ;
this . isStopped = true ;
return ;
}
this . isFinished = true ;
return this . result ;
}
}
2023-06-09 18:50:50 +02:00
async function Generate ( type , { automatic _trigger , force _name2 , resolve , reject , quiet _prompt , force _chid , signal } = { } , dryRun = false ) {
2023-07-20 19:32:15 +02:00
//console.log('Generate entered');
setGenerationProgress ( 0 ) ;
tokens _already _generated = 0 ;
generation _started = new Date ( ) ;
// Don't recreate abort controller if signal is passed
if ( ! ( abortController && signal ) ) {
abortController = new AbortController ( ) ;
}
// OpenAI doesn't need instruct mode. Use OAI main prompt instead.
const isInstruct = power _user . instruct . enabled && main _api !== 'openai' ;
const isImpersonate = type == "impersonate" ;
message _already _generated = isImpersonate ? ` ${ name1 } : ` : ` ${ name2 } : ` ;
// Name for the multigen prefix
2023-09-06 13:07:00 +02:00
const magName = isImpersonate ? name1 : name2 ;
2023-07-20 19:32:15 +02:00
if ( isInstruct ) {
2023-08-22 13:30:49 +02:00
message _already _generated = formatInstructModePrompt ( magName , isImpersonate , '' , name1 , name2 ) ;
2023-07-20 19:32:15 +02:00
} else {
message _already _generated = ` ${ magName } : ` ;
}
// To trim after multigen ended
const magFirst = message _already _generated ;
const interruptedByCommand = processCommands ( $ ( "#send_textarea" ) . val ( ) , type ) ;
if ( interruptedByCommand ) {
$ ( "#send_textarea" ) . val ( '' ) . trigger ( 'input' ) ;
is _send _press = false ;
return ;
}
if ( main _api == 'textgenerationwebui' && textgenerationwebui _settings . streaming && ! textgenerationwebui _settings . streaming _url ) {
toastr . error ( 'Streaming URL is not set. Look it up in the console window when starting TextGen Web UI' ) ;
is _send _press = false ;
return ;
}
2023-09-01 00:07:04 +02:00
if ( main _api == 'kobold' && kai _settings . streaming _kobold && ! kai _flags . can _use _streaming ) {
2023-07-20 19:32:15 +02:00
toastr . error ( 'Streaming is enabled, but the version of Kobold used does not support token streaming.' , undefined , { timeOut : 10000 , preventDuplicates : true , } ) ;
is _send _press = false ;
return ;
}
if ( main _api == 'kobold' && kai _settings . streaming _kobold && power _user . multigen ) {
toastr . error ( 'Multigen is not supported with Kobold streaming enabled. Disable streaming in "AI Response Configuration" or multigen in "Advanced Formatting" to proceed.' , undefined , { timeOut : 10000 , preventDuplicates : true , } ) ;
is _send _press = false ;
return ;
}
if ( isHordeGenerationNotAllowed ( ) ) {
is _send _press = false ;
return ;
}
2023-08-25 18:23:05 +02:00
// Hide swipes if not in a dry run.
if ( ! dryRun ) {
2023-07-20 19:32:15 +02:00
hideSwipeButtons ( ) ;
}
// Set empty promise resolution functions
if ( typeof resolve !== 'function' ) {
resolve = ( ) => { } ;
}
if ( typeof reject !== 'function' ) {
reject = ( ) => { } ;
}
2023-07-13 20:05:55 +02:00
if ( selected _group && ! is _group _generating && ! dryRun ) {
2023-07-20 19:32:15 +02:00
generateGroupWrapper ( false , type , { resolve , reject , quiet _prompt , force _chid , signal : abortController . signal } ) ;
return ;
2023-07-13 20:05:55 +02:00
} else if ( selected _group && ! is _group _generating && dryRun ) {
const characterIndexMap = new Map ( characters . map ( ( char , index ) => [ char . avatar , index ] ) ) ;
const group = groups . find ( ( x ) => x . id === selected _group ) ;
const enabledMembers = group . members . reduce ( ( acc , member ) => {
if ( ! group . disabled _members . includes ( member ) && ! acc . includes ( member ) ) {
acc . push ( member ) ;
}
return acc ;
} , [ ] ) ;
const memberIds = enabledMembers
. map ( ( member ) => characterIndexMap . get ( member ) )
2023-08-19 02:13:40 +02:00
. filter ( ( index ) => index !== undefined && index !== null ) ;
2023-07-13 20:05:55 +02:00
if ( memberIds . length > 0 ) {
setCharacterId ( memberIds [ 0 ] ) ;
setCharacterName ( '' ) ;
2023-08-19 02:13:40 +02:00
} else {
console . log ( 'No enabled members found' ) ;
2023-08-23 20:32:38 +02:00
is _send _press = false ;
2023-08-19 02:13:40 +02:00
return ;
2023-07-13 20:05:55 +02:00
}
2023-07-20 19:32:15 +02:00
}
2023-08-20 11:29:20 +02:00
if ( quiet _prompt ) {
quiet _prompt = substituteParams ( quiet _prompt ) ;
quiet _prompt = main _api == 'novel' ? adjustNovelInstructionPrompt ( quiet _prompt ) : quiet _prompt ;
}
2023-06-09 18:50:50 +02:00
if ( true === dryRun ||
( online _status != 'no_connection' && this _chid != undefined && this _chid !== 'invalid-safety-id' ) ) {
2023-07-20 19:32:15 +02:00
let textareaText ;
2023-08-13 17:43:17 +02:00
if ( type !== 'regenerate' && type !== "swipe" && type !== 'quiet' && ! isImpersonate && ! dryRun ) {
2023-07-20 19:32:15 +02:00
is _send _press = true ;
textareaText = $ ( "#send_textarea" ) . val ( ) ;
$ ( "#send_textarea" ) . val ( '' ) . trigger ( 'input' ) ;
} else {
textareaText = "" ;
if ( chat . length && chat [ chat . length - 1 ] [ 'is_user' ] ) {
//do nothing? why does this check exist?
}
2023-08-27 12:34:29 +02:00
else if ( type !== 'quiet' && type !== "swipe" && ! isImpersonate && ! dryRun && chat . length ) {
2023-07-20 19:32:15 +02:00
chat . length = chat . length - 1 ;
count _view _mes -= 1 ;
2023-08-27 12:34:29 +02:00
$ ( '#chat' ) . children ( ) . last ( ) . hide ( 250 , function ( ) {
2023-07-20 19:32:15 +02:00
$ ( this ) . remove ( ) ;
} ) ;
await eventSource . emit ( event _types . MESSAGE _DELETED , chat . length ) ;
}
}
2023-08-24 14:13:04 +02:00
if ( ! type && ! textareaText && power _user . continue _on _send && ! selected _group && chat . length && ! chat [ chat . length - 1 ] [ 'is_user' ] && ! chat [ chat . length - 1 ] [ 'is_system' ] ) {
type = 'continue' ;
}
2023-07-22 16:46:59 +02:00
const isContinue = type == 'continue' ;
2023-08-14 23:01:55 +02:00
if ( ! dryRun ) {
deactivateSendButtons ( ) ;
}
2023-07-20 19:32:15 +02:00
let { messageBias , promptBias , isUserPromptBias } = getBiasStrings ( textareaText , type ) ;
//*********************************
//PRE FORMATING STRING
//*********************************
//for normal messages sent from user..
if ( textareaText != "" && ! automatic _trigger && type !== 'quiet' ) {
// If user message contains no text other than bias - send as a system message
if ( messageBias && replaceBiasMarkup ( textareaText ) . trim ( ) . length === 0 ) {
sendSystemMessage ( system _message _types . GENERIC , ' ' , { bias : messageBias } ) ;
}
else {
await sendMessageAsUser ( textareaText , messageBias ) ;
}
}
else if ( textareaText == "" && ! automatic _trigger && type === undefined && main _api == 'openai' && oai _settings . send _if _empty . trim ( ) . length > 0 ) {
// Use send_if_empty if set and the user message is empty. Only when sending messages normally
await sendMessageAsUser ( oai _settings . send _if _empty . trim ( ) , messageBias ) ;
}
////////////////////////////////////
const scenarioText = chat _metadata [ 'scenario' ] || characters [ this _chid ] . scenario ;
let charDescription = baseChatReplace ( characters [ this _chid ] . description . trim ( ) , name1 , name2 ) ;
let charPersonality = baseChatReplace ( characters [ this _chid ] . personality . trim ( ) , name1 , name2 ) ;
2023-08-20 16:08:54 +02:00
let personaDescription = baseChatReplace ( power _user . persona _description . trim ( ) , name1 , name2 ) ;
2023-07-20 19:32:15 +02:00
let Scenario = baseChatReplace ( scenarioText . trim ( ) , name1 , name2 ) ;
let mesExamples = baseChatReplace ( characters [ this _chid ] . mes _example . trim ( ) , name1 , name2 ) ;
2023-08-19 20:36:22 +02:00
let systemPrompt = power _user . prefer _character _prompt ? baseChatReplace ( characters [ this _chid ] . data ? . system _prompt ? . trim ( ) , name1 , name2 ) : '' ;
let jailbreakPrompt = power _user . prefer _character _jailbreak ? baseChatReplace ( characters [ this _chid ] . data ? . post _history _instructions ? . trim ( ) , name1 , name2 ) : '' ;
2023-07-20 19:32:15 +02:00
2023-08-20 17:30:19 +02:00
if ( isInstruct ) {
systemPrompt = power _user . prefer _character _prompt && systemPrompt ? systemPrompt : baseChatReplace ( power _user . instruct . system _prompt , name1 , name2 ) ;
2023-08-23 17:04:22 +02:00
systemPrompt = formatInstructModeSystemPrompt ( substituteParams ( systemPrompt , name1 , name2 , power _user . instruct . system _prompt ) ) ;
2023-08-20 17:30:19 +02:00
}
2023-07-20 19:32:15 +02:00
// Parse example messages
if ( ! mesExamples . startsWith ( '<START>' ) ) {
mesExamples = '<START>\n' + mesExamples . trim ( ) ;
}
if ( mesExamples . replace ( /<START>/gi , '' ) . trim ( ) . length === 0 ) {
mesExamples = '' ;
}
2023-08-20 22:03:45 +02:00
if ( mesExamples && isInstruct ) {
mesExamples = formatInstructModeExamples ( mesExamples , name1 , name2 )
}
2023-08-17 23:41:21 +02:00
2023-09-08 12:25:17 +02:00
const exampleSeparator = power _user . context . example _separator ? ` ${ substituteParams ( power _user . context . example _separator ) } \n ` : '' ;
2023-08-18 11:32:52 +02:00
const blockHeading = main _api === 'openai' ? '<START>\n' : exampleSeparator ;
let mesExamplesArray = mesExamples . split ( /<START>/gi ) . slice ( 1 ) . map ( block => ` ${ blockHeading } ${ block . trim ( ) } \n ` ) ;
2023-07-20 19:32:15 +02:00
2023-08-20 18:43:58 +02:00
if ( power _user . strip _examples )
2023-08-19 18:25:07 +02:00
mesExamplesArray = [ ]
2023-07-20 19:32:15 +02:00
// First message in fresh 1-on-1 chat reacts to user/character settings changes
if ( chat . length ) {
chat [ 0 ] . mes = substituteParams ( chat [ 0 ] . mes ) ;
}
// Collect messages with usable content
let coreChat = chat . filter ( x => ! x . is _system ) ;
if ( type === 'swipe' ) {
coreChat . pop ( ) ;
}
// Determine token limit
let this _max _context = getMaxContextSize ( ) ;
2023-08-14 10:43:31 +02:00
if ( ! dryRun ) {
console . debug ( 'Running extension interceptors' ) ;
await runGenerationInterceptors ( coreChat , this _max _context ) ;
} else {
console . debug ( 'Skipping extension interceptors for dry run' ) ;
}
2023-07-20 19:32:15 +02:00
console . log ( ` Core/all messages: ${ coreChat . length } / ${ chat . length } ` ) ;
// kingbri MARK: - Make sure the prompt bias isn't the same as the user bias
2023-09-06 13:07:00 +02:00
if ( ( promptBias && ! isUserPromptBias ) || power _user . always _force _name2 ) {
2023-07-20 19:32:15 +02:00
force _name2 = true ;
}
if ( isImpersonate ) {
force _name2 = false ;
}
//////////////////////////////////
let chat2 = [ ] ;
let continue _mag = '' ;
for ( let i = coreChat . length - 1 , j = 0 ; i >= 0 ; i -- , j ++ ) {
// For OpenAI it's only used in WI
if ( main _api == 'openai' && ( ! world _info || world _info . length === 0 ) ) {
console . debug ( 'No WI, skipping chat2 for OAI' ) ;
break ;
}
2023-08-24 01:22:37 +02:00
chat2 [ i ] = formatMessageHistoryItem ( coreChat [ j ] , isInstruct , false ) ;
2023-07-20 19:32:15 +02:00
2023-08-25 14:01:25 +02:00
if ( j === 0 && isInstruct ) {
2023-08-25 22:02:11 +02:00
// Reformat with the first output sequence (if any)
chat2 [ i ] = formatMessageHistoryItem ( coreChat [ j ] , isInstruct , force _output _sequence . FIRST ) ;
2023-08-25 14:01:25 +02:00
}
2023-07-20 19:32:15 +02:00
// Do not suffix the message for continuation
if ( i === 0 && isContinue ) {
2023-08-24 01:22:37 +02:00
if ( isInstruct ) {
2023-08-25 22:02:11 +02:00
// Reformat with the last output sequence (if any)
chat2 [ i ] = formatMessageHistoryItem ( coreChat [ j ] , isInstruct , force _output _sequence . LAST ) ;
2023-08-24 01:22:37 +02:00
}
2023-07-20 19:32:15 +02:00
chat2 [ i ] = chat2 [ i ] . slice ( 0 , chat2 [ i ] . lastIndexOf ( coreChat [ j ] . mes ) + coreChat [ j ] . mes . length ) ;
continue _mag = coreChat [ j ] . mes ;
}
}
// Adjust token limit for Horde
let adjustedParams ;
if ( main _api == 'koboldhorde' && ( horde _settings . auto _adjust _context _length || horde _settings . auto _adjust _response _length ) ) {
try {
adjustedParams = await adjustHordeGenerationParams ( max _context , amount _gen ) ;
}
catch {
activateSendButtons ( ) ;
return ;
}
if ( horde _settings . auto _adjust _context _length ) {
this _max _context = ( adjustedParams . maxContextLength - adjustedParams . maxLength ) ;
}
}
// Extension added strings
// Set non-WI AN
setFloatingPrompt ( ) ;
// Add WI to prompt (and also inject WI to AN value via hijack)
let { worldInfoString , worldInfoBefore , worldInfoAfter } = await getWorldInfoPrompt ( chat2 , this _max _context ) ;
// Add persona description to prompt
2023-08-23 23:26:47 +02:00
addPersonaDescriptionExtensionPrompt ( ) ;
2023-07-20 19:32:15 +02:00
// Call combined AN into Generate
let allAnchors = getAllExtensionPrompts ( ) ;
2023-09-08 00:26:26 +02:00
const afterScenarioAnchor = getExtensionPrompt ( extension _prompt _types . IN _PROMPT ) ;
2023-07-20 19:32:15 +02:00
let zeroDepthAnchor = getExtensionPrompt ( extension _prompt _types . IN _CHAT , 0 , ' ' ) ;
2023-08-23 23:26:47 +02:00
const storyStringParams = {
description : charDescription ,
personality : charPersonality ,
persona : personaDescription ,
scenario : Scenario ,
system : isInstruct ? systemPrompt : '' ,
char : name2 ,
user : name1 ,
wiBefore : worldInfoBefore ,
wiAfter : worldInfoAfter ,
loreBefore : worldInfoBefore ,
loreAfter : worldInfoAfter ,
} ;
const storyString = renderStoryString ( storyStringParams ) ;
2023-07-20 19:32:15 +02:00
if ( main _api === 'openai' ) {
message _already _generated = '' ; // OpenAI doesn't have multigen
setOpenAIMessages ( coreChat ) ;
setOpenAIMessageExamples ( mesExamplesArray ) ;
}
// hack for regeneration of the first message
if ( chat2 . length == 0 ) {
chat2 . push ( '' ) ;
}
let examplesString = '' ;
let chatString = '' ;
2023-08-30 23:16:58 +02:00
let cyclePrompt = '' ;
2023-07-20 19:32:15 +02:00
function getMessagesTokenCount ( ) {
const encodeString = [
storyString ,
examplesString ,
chatString ,
allAnchors ,
quiet _prompt ,
2023-08-30 23:16:58 +02:00
cyclePrompt ,
2023-07-20 19:32:15 +02:00
] . join ( '' ) . replace ( /\r/gm , '' ) ;
return getTokenCount ( encodeString , power _user . token _padding ) ;
}
// Force pinned examples into the context
let pinExmString ;
if ( power _user . pin _examples ) {
pinExmString = examplesString = mesExamplesArray . join ( '' ) ;
}
if ( isContinue ) {
cyclePrompt = chat2 . shift ( ) ;
}
// Collect enough messages to fill the context
let arrMes = [ ] ;
let tokenCount = getMessagesTokenCount ( ) ;
for ( let item of chat2 ) {
// not needed for OAI prompting
if ( main _api == 'openai' ) {
break ;
}
tokenCount += getTokenCount ( item . replace ( /\r/gm , '' ) )
chatString = item + chatString ;
if ( tokenCount < this _max _context ) {
arrMes [ arrMes . length ] = item ;
} else {
break ;
}
// Prevent UI thread lock on tokenization
await delay ( 1 ) ;
}
if ( main _api !== 'openai' ) {
setInContextMessages ( arrMes . length , type ) ;
}
// Estimate how many unpinned example messages fit in the context
tokenCount = getMessagesTokenCount ( ) ;
let count _exm _add = 0 ;
if ( ! power _user . pin _examples ) {
for ( let example of mesExamplesArray ) {
tokenCount += getTokenCount ( example . replace ( /\r/gm , '' ) )
examplesString += example ;
if ( tokenCount < this _max _context ) {
count _exm _add ++ ;
} else {
break ;
}
await delay ( 1 ) ;
}
}
let mesSend = [ ] ;
console . debug ( 'calling runGenerate' ) ;
2023-08-14 23:01:55 +02:00
if ( ! dryRun ) {
streamingProcessor = isStreamingEnabled ( ) ? new StreamingProcessor ( type , force _name2 ) : false ;
}
2023-07-20 19:32:15 +02:00
if ( isContinue ) {
// Coping mechanism for OAI spacing
if ( ( main _api === 'openai' ) && ! cyclePrompt . endsWith ( ' ' ) ) {
cyclePrompt += ' ' ;
continue _mag += ' ' ;
}
// Save reply does add cycle text to the prompt, so it's not needed here
streamingProcessor && ( streamingProcessor . firstMessageText = '' ) ;
message _already _generated = continue _mag ;
tokens _already _generated = 1 ; // Multigen copium
}
// Multigen rewrites the type and I don't know why
const originalType = type ;
runGenerate ( cyclePrompt ) ;
2023-08-27 18:24:28 +02:00
async function runGenerate ( cycleGenerationPrompt = '' ) {
2023-08-14 23:01:55 +02:00
if ( ! dryRun ) {
is _send _press = true ;
}
2023-07-20 19:32:15 +02:00
2023-08-27 18:24:28 +02:00
generatedPromptCache += cycleGenerationPrompt ;
if ( generatedPromptCache . length == 0 || type === 'continue' ) {
2023-07-20 19:32:15 +02:00
if ( main _api === 'openai' ) {
generateOpenAIPromptCache ( ) ;
}
console . debug ( 'generating prompt' ) ;
chatString = "" ;
arrMes = arrMes . reverse ( ) ;
2023-08-25 00:02:17 +02:00
arrMes . forEach ( function ( item , i , arr ) { // For added anchors and others
2023-07-20 19:32:15 +02:00
// OAI doesn't need all of this
if ( main _api === 'openai' ) {
return ;
}
2023-08-24 01:22:37 +02:00
// Cohee: I'm not even sure what this is for anymore
if ( i === arrMes . length - 1 && type !== 'continue' ) {
2023-08-03 06:21:38 +02:00
item = item . replace ( /\n?$/ , '' ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-25 00:02:17 +02:00
mesSend [ mesSend . length ] = { message : item , extensionPrompts : [ ] } ;
2023-07-20 19:32:15 +02:00
} ) ;
}
let mesExmString = '' ;
2023-08-27 18:24:28 +02:00
function setPromptString ( ) {
2023-07-20 19:32:15 +02:00
if ( main _api == 'openai' ) {
return ;
}
console . debug ( '--setting Prompt string' ) ;
mesExmString = pinExmString ? ? mesExamplesArray . slice ( 0 , count _exm _add ) . join ( '' ) ;
2023-08-24 14:13:04 +02:00
if ( mesSend . length ) {
2023-08-25 00:02:17 +02:00
mesSend [ mesSend . length - 1 ] . message = modifyLastPromptLine ( mesSend [ mesSend . length - 1 ] . message ) ;
2023-08-24 14:13:04 +02:00
}
2023-07-20 19:32:15 +02:00
}
2023-08-20 06:44:39 +02:00
function modifyLastPromptLine ( lastMesString ) {
2023-07-20 19:32:15 +02:00
// Add quiet generation prompt at depth 0
if ( quiet _prompt && quiet _prompt . length ) {
2023-09-06 13:07:00 +02:00
const name = name1 ;
2023-09-13 10:52:37 +02:00
const quietAppend = isInstruct ? formatInstructModeChat ( name , quiet _prompt , false , true , '' , name1 , name2 , false ) : ` \n ${ quiet _prompt } ` ;
2023-08-20 06:44:39 +02:00
lastMesString += quietAppend ;
2023-07-20 19:32:15 +02:00
// Bail out early
2023-08-20 06:44:39 +02:00
return lastMesString ;
2023-07-20 19:32:15 +02:00
}
// Get instruct mode line
if ( isInstruct && tokens _already _generated === 0 ) {
2023-09-06 13:07:00 +02:00
const name = isImpersonate ? name1 : name2 ;
2023-08-20 06:44:39 +02:00
lastMesString += formatInstructModePrompt ( name , isImpersonate , promptBias , name1 , name2 ) ;
2023-07-20 19:32:15 +02:00
}
// Get non-instruct impersonation line
if ( ! isInstruct && isImpersonate && tokens _already _generated === 0 ) {
2023-09-06 13:07:00 +02:00
const name = name1 ;
2023-08-20 06:44:39 +02:00
if ( ! lastMesString . endsWith ( '\n' ) ) {
lastMesString += '\n' ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 06:44:39 +02:00
lastMesString += name + ':' ;
2023-07-20 19:32:15 +02:00
}
// Add character's name
2023-08-20 23:36:35 +02:00
// Force name append on continue
if ( ! isInstruct && force _name2 && ( tokens _already _generated === 0 || isContinue ) ) {
2023-08-20 06:44:39 +02:00
if ( ! lastMesString . endsWith ( '\n' ) ) {
lastMesString += '\n' ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 23:36:35 +02:00
lastMesString += ` ${ name2 } : ` ;
2023-08-22 19:34:20 +02:00
}
2023-07-20 19:32:15 +02:00
2023-08-20 06:44:39 +02:00
return lastMesString ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 23:36:35 +02:00
// Clean up the already generated prompt for seamless addition
function cleanupPromptCache ( promptCache ) {
// Remove the first occurrance of character's name
if ( promptCache . trimStart ( ) . startsWith ( ` ${ name2 } : ` ) ) {
promptCache = promptCache . replace ( ` ${ name2 } : ` , '' ) . trimStart ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 23:36:35 +02:00
// Remove the first occurrance of prompt bias
if ( promptCache . trimStart ( ) . startsWith ( promptBias ) ) {
promptCache = promptCache . replace ( promptBias , '' ) ;
}
2023-08-24 01:38:02 +02:00
// Add a space if prompt cache doesn't start with one
if ( ! /^\s/ . test ( promptCache ) && ! isInstruct ) {
promptCache = ' ' + promptCache ;
}
2023-08-20 23:36:35 +02:00
return promptCache ;
2023-07-20 19:32:15 +02:00
}
2023-08-27 18:24:28 +02:00
function checkPromptSize ( ) {
2023-07-20 19:32:15 +02:00
console . debug ( '---checking Prompt size' ) ;
2023-08-27 18:24:28 +02:00
setPromptString ( ) ;
2023-07-20 19:32:15 +02:00
const prompt = [
storyString ,
mesExmString ,
2023-08-20 06:44:39 +02:00
mesSend . join ( '' ) ,
2023-08-27 18:24:28 +02:00
generatedPromptCache ,
2023-07-20 19:32:15 +02:00
allAnchors ,
quiet _prompt ,
] . join ( '' ) . replace ( /\r/gm , '' ) ;
2023-08-27 18:24:28 +02:00
let thisPromptContextSize = getTokenCount ( prompt , power _user . token _padding ) ;
2023-07-20 19:32:15 +02:00
2023-08-27 18:24:28 +02:00
if ( thisPromptContextSize > this _max _context ) { //if the prepared prompt is larger than the max context size...
2023-07-20 19:32:15 +02:00
if ( count _exm _add > 0 ) { // ..and we have example mesages..
count _exm _add -- ; // remove the example messages...
2023-08-27 18:24:28 +02:00
checkPromptSize ( ) ; // and try agin...
2023-07-20 19:32:15 +02:00
} else if ( mesSend . length > 0 ) { // if the chat history is longer than 0
mesSend . shift ( ) ; // remove the first (oldest) chat entry..
2023-08-27 18:24:28 +02:00
checkPromptSize ( ) ; // and check size again..
2023-07-20 19:32:15 +02:00
} else {
//end
console . debug ( ` ---mesSend.length = ${ mesSend . length } ` ) ;
}
}
}
2023-08-27 18:24:28 +02:00
if ( generatedPromptCache . length > 0 && main _api !== 'openai' ) {
console . debug ( '---Generated Prompt Cache length: ' + generatedPromptCache . length ) ;
checkPromptSize ( ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-08-27 18:24:28 +02:00
console . debug ( '---calling setPromptString ' + generatedPromptCache . length )
setPromptString ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 08:19:25 +02:00
// Fetches the combined prompt for both negative and positive prompts
2023-08-20 06:44:39 +02:00
const cfgGuidanceScale = getGuidanceScale ( ) ;
2023-08-25 00:02:17 +02:00
// For prompt bit itemization
let mesSendString = '' ;
2023-08-20 06:44:39 +02:00
function getCombinedPrompt ( isNegative ) {
2023-08-23 17:27:58 +02:00
// Only return if the guidance scale doesn't exist or the value is 1
// Also don't return if constructing the neutral prompt
if ( isNegative && ( ! cfgGuidanceScale || cfgGuidanceScale ? . value === 1 ) ) {
return ;
}
2023-08-25 16:05:51 +02:00
// OAI has its own prompt manager. No need to do anything here
if ( main _api === 'openai' ) {
return ''
}
2023-08-25 00:02:17 +02:00
// Deep clone
let finalMesSend = structuredClone ( mesSend ) ;
// TODO: Rewrite getExtensionPrompt to not require multiple for loops
// Set all extension prompts where insertion depth > mesSend length
2023-08-25 16:05:51 +02:00
if ( finalMesSend . length ) {
for ( let upperDepth = 100 ; upperDepth >= finalMesSend . length ; upperDepth -- ) {
const upperAnchor = getExtensionPrompt ( extension _prompt _types . IN _CHAT , upperDepth ) ;
if ( upperAnchor && upperAnchor . length ) {
finalMesSend [ 0 ] . extensionPrompts . push ( upperAnchor ) ;
}
2023-08-25 00:02:17 +02:00
}
}
finalMesSend . forEach ( ( mesItem , index ) => {
if ( index === 0 ) {
return ;
}
2023-08-30 01:09:30 +02:00
const anchorDepth = Math . abs ( index - finalMesSend . length ) ;
2023-08-25 00:02:17 +02:00
// NOTE: Depth injected here!
const extensionAnchor = getExtensionPrompt ( extension _prompt _types . IN _CHAT , anchorDepth ) ;
2023-08-25 15:46:54 +02:00
2023-08-30 01:09:30 +02:00
if ( anchorDepth >= 0 && extensionAnchor && extensionAnchor . length ) {
2023-08-25 00:02:17 +02:00
mesItem . extensionPrompts . push ( extensionAnchor ) ;
}
} ) ;
// TODO: Move zero-depth anchor append to work like CFG and bias appends
if ( zeroDepthAnchor && zeroDepthAnchor . length ) {
if ( ! isMultigenEnabled ( ) || tokens _already _generated == 0 ) {
console . log ( /\s/ . test ( finalMesSend [ finalMesSend . length - 1 ] . message . slice ( - 1 ) ) )
finalMesSend [ finalMesSend . length - 1 ] . message +=
/\s/ . test ( finalMesSend [ finalMesSend . length - 1 ] . message . slice ( - 1 ) )
? zeroDepthAnchor
: ` ${ zeroDepthAnchor } ` ;
}
}
2023-08-20 08:19:25 +02:00
let cfgPrompt = { } ;
if ( cfgGuidanceScale && cfgGuidanceScale ? . value !== 1 ) {
cfgPrompt = getCfgPrompt ( cfgGuidanceScale , isNegative ) ;
}
2023-08-20 23:36:35 +02:00
if ( cfgPrompt && cfgPrompt ? . value ) {
if ( cfgPrompt ? . depth === 0 ) {
2023-08-25 00:02:17 +02:00
finalMesSend [ finalMesSend . length - 1 ] . message +=
/\s/ . test ( finalMesSend [ finalMesSend . length - 1 ] . message . slice ( - 1 ) )
2023-08-22 19:34:20 +02:00
? cfgPrompt . value
: ` ${ cfgPrompt . value } ` ;
2023-08-20 08:19:25 +02:00
} else {
2023-08-23 17:27:58 +02:00
// TODO: Make all extension prompts use an array/splice method
2023-08-25 00:02:17 +02:00
finalMesSend [ mesSend . length - cfgPrompt . depth ] . extensionPrompts . push ( ` ${ cfgPrompt . value } \n ` ) ;
2023-08-20 06:44:39 +02:00
}
}
2023-08-04 03:22:46 +02:00
2023-08-20 23:36:35 +02:00
// Add prompt bias after everything else
// Always run with continue
2023-08-22 19:34:20 +02:00
if ( ! isInstruct && ! isImpersonate && ( tokens _already _generated === 0 || isContinue ) ) {
2023-08-21 22:52:58 +02:00
if ( promptBias . trim ( ) . length !== 0 ) {
2023-08-25 00:02:17 +02:00
finalMesSend [ finalMesSend . length - 1 ] . message +=
/\s/ . test ( finalMesSend [ finalMesSend . length - 1 ] . message . slice ( - 1 ) )
2023-08-22 19:34:20 +02:00
? promptBias . trimStart ( )
: ` ${ promptBias . trimStart ( ) } ` ;
2023-08-21 22:52:58 +02:00
}
2023-08-22 19:34:20 +02:00
}
2023-08-04 03:22:46 +02:00
2023-08-20 23:36:35 +02:00
// Prune from prompt cache if it exists
2023-08-27 18:24:28 +02:00
if ( generatedPromptCache . length !== 0 ) {
generatedPromptCache = cleanupPromptCache ( generatedPromptCache ) ;
2023-08-20 23:36:35 +02:00
}
2023-07-20 19:32:15 +02:00
2023-08-25 00:02:17 +02:00
// Right now, everything is suffixed with a newline
mesSendString = finalMesSend . map ( ( e ) => ` ${ e . extensionPrompts . join ( '' ) } ${ e . message } ` ) . join ( '' ) ;
2023-07-20 19:32:15 +02:00
2023-08-20 06:44:39 +02:00
// add chat preamble
mesSendString = addChatsPreamble ( mesSendString ) ;
2023-07-20 19:32:15 +02:00
2023-08-20 06:44:39 +02:00
// add a custom dingus (if defined)
mesSendString = addChatsSeparator ( mesSendString ) ;
let combinedPrompt =
storyString +
afterScenarioAnchor +
mesExmString +
mesSendString +
2023-08-27 18:24:28 +02:00
generatedPromptCache ;
2023-07-20 19:32:15 +02:00
2023-08-20 06:44:39 +02:00
combinedPrompt = combinedPrompt . replace ( /\r/gm , '' ) ;
if ( power _user . collapse _newlines ) {
combinedPrompt = collapseNewlines ( combinedPrompt ) ;
}
2023-07-20 19:32:15 +02:00
2023-08-20 06:44:39 +02:00
return combinedPrompt ;
2023-07-20 19:32:15 +02:00
}
2023-08-20 06:44:39 +02:00
2023-08-20 08:19:25 +02:00
// Get the negative prompt first since it has the unmodified mesSend array
2023-08-21 01:51:01 +02:00
let negativePrompt = main _api == 'textgenerationwebui' ? getCombinedPrompt ( true ) : undefined ;
2023-08-25 16:05:51 +02:00
let finalPrompt = getCombinedPrompt ( false ) ;
2023-08-22 16:17:20 +02:00
// Include the entire guidance scale object
2023-08-23 20:34:20 +02:00
const cfgValues = cfgGuidanceScale && cfgGuidanceScale ? . value !== 1 ? ( { guidanceScale : cfgGuidanceScale , negativePrompt : negativePrompt } ) : null ;
2023-08-20 06:44:39 +02:00
2023-08-23 17:26:56 +02:00
let this _amount _gen = Number ( amount _gen ) ; // how many tokens the AI will be requested to generate
2023-07-20 19:32:15 +02:00
let this _settings = koboldai _settings [ koboldai _setting _names [ preset _settings ] ] ;
if ( isMultigenEnabled ( ) && type !== 'quiet' ) {
// if nothing has been generated yet..
this _amount _gen = getMultigenAmount ( ) ;
}
let thisPromptBits = [ ] ;
2023-08-18 05:51:17 +02:00
// TODO: Make this a switch
2023-07-20 19:32:15 +02:00
if ( main _api == 'koboldhorde' && horde _settings . auto _adjust _response _length ) {
this _amount _gen = Math . min ( this _amount _gen , adjustedParams . maxLength ) ;
this _amount _gen = Math . max ( this _amount _gen , MIN _AMOUNT _GEN ) ; // prevent validation errors
}
let generate _data ;
if ( main _api == 'koboldhorde' || main _api == 'kobold' ) {
generate _data = {
2023-08-25 16:05:51 +02:00
prompt : finalPrompt ,
2023-07-20 19:32:15 +02:00
gui _settings : true ,
max _length : amount _gen ,
temperature : kai _settings . temp ,
max _context _length : max _context ,
singleline : kai _settings . single _line ,
} ;
if ( preset _settings != 'gui' ) {
const maxContext = ( adjustedParams && horde _settings . auto _adjust _context _length ) ? adjustedParams . maxContextLength : max _context ;
2023-08-25 16:05:51 +02:00
generate _data = getKoboldGenerationData ( finalPrompt , this _settings , this _amount _gen , maxContext , isImpersonate , type ) ;
2023-07-20 19:32:15 +02:00
}
}
else if ( main _api == 'textgenerationwebui' ) {
2023-08-25 16:05:51 +02:00
generate _data = getTextGenGenerationData ( finalPrompt , this _amount _gen , isImpersonate , cfgValues ) ;
2023-08-03 05:25:24 +02:00
generate _data . use _mancer = api _use _mancer _webui ;
2023-07-20 19:32:15 +02:00
}
else if ( main _api == 'novel' ) {
const this _settings = novelai _settings [ novelai _setting _names [ nai _settings . preset _settings _novel ] ] ;
2023-08-25 16:05:51 +02:00
generate _data = getNovelGenerationData ( finalPrompt , this _settings , this _amount _gen , isImpersonate , cfgValues ) ;
2023-07-20 19:32:15 +02:00
}
else if ( main _api == 'openai' ) {
2023-06-24 19:53:33 +02:00
let [ prompt , counts ] = prepareOpenAIMessages ( {
2023-07-20 19:32:15 +02:00
name2 : name2 ,
2023-06-10 20:09:48 +02:00
charDescription : charDescription ,
charPersonality : charPersonality ,
Scenario : Scenario ,
2023-07-20 19:32:15 +02:00
worldInfoBefore : worldInfoBefore ,
worldInfoAfter : worldInfoAfter ,
2023-06-11 17:17:38 +02:00
extensionPrompts : extension _prompts ,
2023-07-20 19:32:15 +02:00
bias : promptBias ,
type : type ,
quietPrompt : quiet _prompt ,
cyclePrompt : cyclePrompt ,
2023-08-19 20:36:22 +02:00
systemPromptOverride : systemPrompt ,
jailbreakPromptOverride : jailbreakPrompt ,
2023-08-23 20:40:26 +02:00
personaDescription : personaDescription
2023-06-27 20:40:17 +02:00
} , dryRun ) ;
2023-07-20 19:32:15 +02:00
generate _data = { prompt : prompt } ;
// counts will return false if the user has not enabled the token breakdown feature
if ( counts ) {
parseTokenCounts ( counts , thisPromptBits ) ;
}
2023-08-15 14:08:42 +02:00
if ( ! dryRun ) {
setInContextMessages ( openai _messages _count , type ) ;
}
2023-07-20 19:32:15 +02:00
}
2023-08-18 15:45:40 +02:00
if ( true === dryRun ) return onSuccess ( { error : 'dryRun' } ) ;
2023-06-09 18:50:50 +02:00
2023-07-20 19:32:15 +02:00
if ( power _user . console _log _prompts ) {
console . log ( generate _data . prompt ) ;
}
let generate _url = getGenerateUrl ( ) ;
console . debug ( 'rungenerate calling API' ) ;
showStopButton ( ) ;
//set array object for prompt token itemization of this message
let currentArrayEntry = Number ( thisPromptBits . length - 1 ) ;
let additionalPromptStuff = {
... thisPromptBits [ currentArrayEntry ] ,
2023-08-21 22:50:38 +02:00
rawPrompt : generate _data . prompt || generate _data . input ,
2023-07-20 19:32:15 +02:00
mesId : getNextMessageId ( type ) ,
allAnchors : allAnchors ,
summarizeString : ( extension _prompts [ '1_memory' ] ? . value || '' ) ,
authorsNoteString : ( extension _prompts [ '2_floating_prompt' ] ? . value || '' ) ,
smartContextString : ( extension _prompts [ 'chromadb' ] ? . value || '' ) ,
worldInfoString : worldInfoString ,
storyString : storyString ,
afterScenarioAnchor : afterScenarioAnchor ,
examplesString : examplesString ,
2023-08-25 00:02:17 +02:00
mesSendString : mesSendString ,
2023-08-27 18:24:28 +02:00
generatedPromptCache : generatedPromptCache ,
2023-07-20 19:32:15 +02:00
promptBias : promptBias ,
2023-08-27 18:24:28 +02:00
finalPrompt : finalPrompt ,
2023-07-20 19:32:15 +02:00
charDescription : charDescription ,
charPersonality : charPersonality ,
scenarioText : scenarioText ,
this _max _context : this _max _context ,
padding : power _user . token _padding ,
main _api : main _api ,
instruction : isInstruct ? substituteParams ( power _user . prefer _character _prompt && systemPrompt ? systemPrompt : power _user . instruct . system _prompt ) : '' ,
userPersona : ( power _user . persona _description || '' ) ,
} ;
thisPromptBits = additionalPromptStuff ;
//console.log(thisPromptBits);
itemizedPrompts . push ( thisPromptBits ) ;
2023-09-05 17:23:24 +02:00
console . debug ( ` pushed prompt bits to itemizedPrompts array. Length is now: ${ itemizedPrompts . length } ` ) ;
2023-07-20 19:32:15 +02:00
if ( main _api == 'openai' ) {
if ( isStreamingEnabled ( ) && type !== 'quiet' ) {
streamingProcessor . generator = await sendOpenAIRequest ( type , generate _data . prompt , streamingProcessor . abortController . signal ) ;
}
else {
sendOpenAIRequest ( type , generate _data . prompt , abortController . signal ) . then ( onSuccess ) . catch ( onError ) ;
}
}
else if ( main _api == 'koboldhorde' ) {
2023-08-25 16:05:51 +02:00
generateHorde ( finalPrompt , generate _data , abortController . signal ) . then ( onSuccess ) . catch ( onError ) ;
2023-07-20 19:32:15 +02:00
}
else if ( main _api == 'textgenerationwebui' && isStreamingEnabled ( ) && type !== 'quiet' ) {
streamingProcessor . generator = await generateTextGenWithStreaming ( generate _data , streamingProcessor . abortController . signal ) ;
}
else if ( main _api == 'novel' && isStreamingEnabled ( ) && type !== 'quiet' ) {
streamingProcessor . generator = await generateNovelWithStreaming ( generate _data , streamingProcessor . abortController . signal ) ;
}
else if ( main _api == 'kobold' && isStreamingEnabled ( ) && type !== 'quiet' ) {
streamingProcessor . generator = await generateKoboldWithStreaming ( generate _data , streamingProcessor . abortController . signal ) ;
}
else {
try {
const response = await fetch ( generate _url , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
cache : 'no-cache' ,
body : JSON . stringify ( generate _data ) ,
signal : abortController . signal ,
} ) ;
if ( ! response . ok ) {
2023-08-01 14:22:51 +02:00
const error = await response . json ( ) ;
throw error ;
2023-07-20 19:32:15 +02:00
}
const data = await response . json ( ) ;
onSuccess ( data ) ;
} catch ( error ) {
onError ( error ) ;
}
}
if ( isStreamingEnabled ( ) && type !== 'quiet' ) {
hideSwipeButtons ( ) ;
let getMessage = await streamingProcessor . generate ( ) ;
if ( isContinue ) {
getMessage = continue _mag + getMessage ;
}
if ( streamingProcessor && ! streamingProcessor . isStopped && streamingProcessor . isFinished ) {
2023-08-22 16:46:37 +02:00
await streamingProcessor . onFinishStreaming ( streamingProcessor . messageId , getMessage ) ;
2023-07-20 19:32:15 +02:00
streamingProcessor = null ;
}
}
async function onSuccess ( data ) {
2023-08-14 23:01:55 +02:00
if ( data . error == 'dryRun' ) {
2023-08-27 18:24:28 +02:00
generatedPromptCache = '' ;
2023-08-14 23:01:55 +02:00
resolve ( ) ;
return ;
}
2023-07-20 19:32:15 +02:00
if ( ! data . error ) {
//const getData = await response.json();
let getMessage = extractMessageFromData ( data ) ;
let title = extractTitleFromData ( data ) ;
kobold _horde _model = title ;
// to make it continue generating so long as it's under max_amount and hasn't signaled
// an end to the character's response via typing "You:" or adding "<endoftext>"
if ( isMultigenEnabled ( ) && type !== 'quiet' ) {
message _already _generated += getMessage ;
promptBias = '' ;
let this _mes _is _name ;
( { this _mes _is _name , getMessage } = extractNameFromMessage ( getMessage , force _name2 , isImpersonate ) ) ;
if ( ! isImpersonate ) {
if ( tokens _already _generated == 0 ) {
console . debug ( "New message" ) ;
2023-08-22 16:46:37 +02:00
( { type , getMessage } = await saveReply ( type , getMessage , this _mes _is _name , title ) ) ;
2023-07-20 19:32:15 +02:00
}
else {
console . debug ( "Should append message" ) ;
2023-08-22 16:46:37 +02:00
( { type , getMessage } = await saveReply ( 'append' , getMessage , this _mes _is _name , title ) ) ;
2023-07-20 19:32:15 +02:00
}
} else {
let chunk = cleanUpMessage ( message _already _generated , true , isContinue , true ) ;
let extract = extractNameFromMessage ( chunk , force _name2 , isImpersonate ) ;
$ ( '#send_textarea' ) . val ( extract . getMessage ) . trigger ( 'input' ) ;
}
if ( shouldContinueMultigen ( getMessage , isImpersonate , isInstruct ) ) {
hideSwipeButtons ( ) ;
tokens _already _generated += this _amount _gen ; // add new gen amt to any prev gen counter..
getMessage = message _already _generated ;
// if any tokens left to generate
if ( getMultigenAmount ( ) > 0 ) {
runGenerate ( getMessage ) ;
console . debug ( 'returning to make generate again' ) ;
return ;
}
}
tokens _already _generated = 0 ;
2023-08-27 18:24:28 +02:00
generatedPromptCache = "" ;
2023-07-20 19:32:15 +02:00
const substringStart = originalType !== 'continue' ? magFirst . length : 0 ;
getMessage = message _already _generated . substring ( substringStart ) ;
}
if ( isContinue ) {
getMessage = continue _mag + getMessage ;
}
//Formating
2023-08-09 15:41:12 +02:00
const displayIncomplete = type == 'quiet' ;
getMessage = cleanUpMessage ( getMessage , isImpersonate , isContinue , displayIncomplete ) ;
2023-07-20 19:32:15 +02:00
let this _mes _is _name ;
( { this _mes _is _name , getMessage } = extractNameFromMessage ( getMessage , force _name2 , isImpersonate ) ) ;
if ( getMessage . length > 0 ) {
if ( isImpersonate ) {
$ ( '#send_textarea' ) . val ( getMessage ) . trigger ( 'input' ) ;
2023-08-27 18:24:28 +02:00
generatedPromptCache = "" ;
2023-07-20 19:32:15 +02:00
await eventSource . emit ( event _types . IMPERSONATE _READY , getMessage ) ;
}
else if ( type == 'quiet' ) {
resolve ( getMessage ) ;
}
else {
// Without streaming we'll be having a full message on continuation. Treat it as a multigen last chunk.
if ( ! isMultigenEnabled ( ) && originalType !== 'continue' ) {
2023-08-22 16:46:37 +02:00
( { type , getMessage } = await saveReply ( type , getMessage , this _mes _is _name , title ) ) ;
2023-07-20 19:32:15 +02:00
}
else {
2023-08-22 16:46:37 +02:00
( { type , getMessage } = await saveReply ( 'appendFinal' , getMessage , this _mes _is _name , title ) ) ;
2023-07-20 19:32:15 +02:00
}
}
activateSendButtons ( ) ;
if ( type !== 'quiet' ) {
playMessageSound ( ) ;
}
generate _loop _counter = 0 ;
} else {
++ generate _loop _counter ;
if ( generate _loop _counter > MAX _GENERATION _LOOPS ) {
throwCircuitBreakerError ( ) ;
}
// regenerate with character speech reenforced
// to make sure we leave on swipe type while also adding the name2 appendage
setTimeout ( ( ) => {
Generate ( type , { automatic _trigger , force _name2 : true , resolve , reject , quiet _prompt , force _chid } ) ;
} , generate _loop _counter * 1000 ) ;
}
if ( power _user . auto _swipe ) {
console . debug ( 'checking for autoswipeblacklist on non-streaming message' ) ;
function containsBlacklistedWords ( getMessage , blacklist , threshold ) {
console . debug ( 'checking blacklisted words' ) ;
const regex = new RegExp ( ` \\ b( ${ blacklist . join ( '|' ) } ) \\ b ` , 'gi' ) ;
const matches = getMessage . match ( regex ) || [ ] ;
return matches . length >= threshold ;
}
const generatedTextFiltered = ( getMessage ) => {
if ( power _user . auto _swipe _blacklist _threshold ) {
if ( containsBlacklistedWords ( getMessage , power _user . auto _swipe _blacklist , power _user . auto _swipe _blacklist _threshold ) ) {
console . debug ( "Generated text has blacklisted words" )
return true
}
}
return false
}
if ( generatedTextFiltered ( getMessage ) ) {
console . debug ( 'swiping right automatically' ) ;
2023-08-23 09:32:48 +02:00
is _send _press = false ;
2023-07-20 19:32:15 +02:00
swipe _right ( ) ;
return
}
}
} else {
2023-08-27 18:24:28 +02:00
generatedPromptCache = '' ;
2023-07-20 19:32:15 +02:00
activateSendButtons ( ) ;
//console.log('runGenerate calling showSwipeBtns');
showSwipeButtons ( ) ;
2023-08-03 05:25:24 +02:00
if ( main _api == 'textgenerationwebui' && api _use _mancer _webui ) {
const errorText = ` <h3>Inferencer endpoint is unhappy!</h3>
2023-08-03 13:32:53 +02:00
Returned status < tt > $ { data . status } < /tt> with the reason:<br/ >
$ { data . response } ` ;
2023-08-03 05:25:24 +02:00
callPopup ( errorText , 'text' ) ;
}
2023-07-20 19:32:15 +02:00
}
console . debug ( '/savechat called by /Generate' ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
is _send _press = false ;
hideStopButton ( ) ;
2023-07-20 19:32:15 +02:00
activateSendButtons ( ) ;
showSwipeButtons ( ) ;
setGenerationProgress ( 0 ) ;
if ( type !== 'quiet' ) {
resolve ( ) ;
}
} ;
function onError ( exception ) {
2023-08-01 14:22:51 +02:00
if ( typeof exception ? . error ? . message === 'string' ) {
toastr . error ( exception . error . message , 'Error' , { timeOut : 10000 , extendedTimeOut : 20000 } ) ;
}
2023-07-20 19:32:15 +02:00
reject ( exception ) ;
$ ( "#send_textarea" ) . removeAttr ( 'disabled' ) ;
is _send _press = false ;
activateSendButtons ( ) ;
showSwipeButtons ( ) ;
setGenerationProgress ( 0 ) ;
console . log ( exception ) ;
} ;
} //rungenerate ends
} else { //generate's primary loop ends, after this is error handling for no-connection or safety-id
if ( this _chid === undefined || this _chid === 'invalid-safety-id' ) {
toastr . warning ( 'С haracter is not selected' ) ;
}
is _send _press = false ;
}
//console.log('generate ending');
} //generate ends
function getNextMessageId ( type ) {
return type == 'swipe' ? Number ( count _view _mes - 1 ) : Number ( count _view _mes ) ;
}
export function getBiasStrings ( textareaText , type ) {
2023-07-25 05:45:14 +02:00
if ( type == 'impersonate' || type == 'continue' ) {
2023-07-20 19:32:15 +02:00
return { messageBias : '' , promptBias : '' , isUserPromptBias : false } ;
}
let promptBias = '' ;
let messageBias = extractMessageBias ( textareaText ) ;
// If user input is not provided, retrieve the bias of the most recent relevant message
if ( ! textareaText ) {
for ( let i = chat . length - 1 ; i >= 0 ; i -- ) {
const mes = chat [ i ] ;
if ( type === 'swipe' && chat . length - 1 === i ) {
continue ;
}
if ( mes && ( mes . is _user || mes . is _system || mes . extra ? . type === system _message _types . NARRATOR ) ) {
if ( mes . extra ? . bias ? . trim ( ) ? . length > 0 ) {
promptBias = mes . extra . bias ;
}
break ;
}
}
}
promptBias = messageBias || promptBias || power _user . user _prompt _bias || '' ;
const isUserPromptBias = promptBias === power _user . user _prompt _bias ;
// Substitute params for everything
messageBias = substituteParams ( messageBias ) ;
promptBias = substituteParams ( promptBias ) ;
return { messageBias , promptBias , isUserPromptBias } ;
}
2023-08-24 01:22:37 +02:00
/ * *
* @ param { Object } chatItem Message history item .
* @ param { boolean } isInstruct Whether instruct mode is enabled .
2023-08-25 22:02:11 +02:00
* @ param { boolean | number } forceOutputSequence Whether to force the first / last output sequence for instruct mode .
2023-08-24 01:22:37 +02:00
* /
2023-08-25 22:02:11 +02:00
function formatMessageHistoryItem ( chatItem , isInstruct , forceOutputSequence ) {
2023-07-20 19:32:15 +02:00
const isNarratorType = chatItem ? . extra ? . type === system _message _types . NARRATOR ;
const characterName = ( selected _group || chatItem . force _avatar ) ? chatItem . name : name2 ;
const itemName = chatItem . is _user ? chatItem [ 'name' ] : characterName ;
const shouldPrependName = ( chatItem . is _name || chatItem . force _avatar || selected _group ) && ! isNarratorType ;
let textResult = shouldPrependName ? ` ${ itemName } : ${ chatItem . mes } \n ` : ` ${ chatItem . mes } \n ` ;
if ( isInstruct ) {
2023-08-25 22:02:11 +02:00
textResult = formatInstructModeChat ( itemName , chatItem . mes , chatItem . is _user , isNarratorType , chatItem . force _avatar , name1 , name2 , forceOutputSequence ) ;
2023-07-20 19:32:15 +02:00
}
textResult = replaceBiasMarkup ( textResult ) ;
return textResult ;
}
export function replaceBiasMarkup ( str ) {
return ( str ? ? '' ) . replace ( /\{\{[\s\S]*?\}\}/gm , '' ) ;
}
export async function sendMessageAsUser ( textareaText , messageBias ) {
textareaText = getRegexedString ( textareaText , regex _placement . USER _INPUT ) ;
chat [ chat . length ] = { } ;
chat [ chat . length - 1 ] [ 'name' ] = name1 ;
chat [ chat . length - 1 ] [ 'is_user' ] = true ;
chat [ chat . length - 1 ] [ 'is_name' ] = true ;
chat [ chat . length - 1 ] [ 'send_date' ] = getMessageTimeStamp ( ) ;
chat [ chat . length - 1 ] [ 'mes' ] = substituteParams ( textareaText ) ;
chat [ chat . length - 1 ] [ 'extra' ] = { } ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = getTokenCount ( chat [ chat . length - 1 ] [ 'mes' ] , 0 ) ;
}
2023-07-20 19:32:15 +02:00
// Lock user avatar to a persona.
if ( user _avatar in power _user . personas ) {
chat [ chat . length - 1 ] [ 'force_avatar' ] = getUserAvatar ( user _avatar ) ;
}
if ( messageBias ) {
console . debug ( 'checking bias' ) ;
chat [ chat . length - 1 ] [ 'extra' ] [ 'bias' ] = messageBias ;
}
statMesProcess ( chat [ chat . length - 1 ] , 'user' , characters , this _chid , '' ) ;
// Wait for all handlers to finish before continuing with the prompt
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
2023-08-22 16:46:37 +02:00
addOneMessage ( chat [ chat . length - 1 ] ) ;
2023-08-22 21:45:12 +02:00
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
console . debug ( 'message sent as user' ) ;
}
function getMaxContextSize ( ) {
let this _max _context = 1487 ;
if ( main _api == 'kobold' || main _api == 'koboldhorde' || main _api == 'textgenerationwebui' ) {
this _max _context = ( max _context - amount _gen ) ;
}
if ( main _api == 'novel' ) {
this _max _context = Number ( max _context ) ;
2023-09-06 13:32:06 +02:00
if ( nai _settings . model _novel . includes ( 'clio' ) ) {
2023-07-20 19:32:15 +02:00
this _max _context = Math . min ( max _context , 8192 ) ;
}
2023-09-06 13:32:06 +02:00
if ( nai _settings . model _novel . includes ( 'kayra' ) ) {
2023-08-12 13:21:05 +02:00
this _max _context = Math . min ( max _context , 8192 ) ;
2023-08-10 18:15:52 +02:00
2023-08-12 13:21:05 +02:00
const subscriptionLimit = getKayraMaxContextTokens ( ) ;
if ( typeof subscriptionLimit === "number" && this _max _context > subscriptionLimit ) {
this _max _context = subscriptionLimit ;
console . log ( ` NovelAI subscription limit reached. Max context size is now ${ this _max _context } ` ) ;
}
2023-08-10 18:15:52 +02:00
}
this _max _context = this _max _context - amount _gen ;
2023-07-20 19:32:15 +02:00
}
if ( main _api == 'openai' ) {
this _max _context = oai _settings . openai _max _context ;
}
return this _max _context ;
}
function parseTokenCounts ( counts , thisPromptBits ) {
2023-08-22 19:34:20 +02:00
/ * *
* @ param { any [ ] } numbers
* /
function getSum ( ... numbers ) {
return numbers . map ( x => Number ( x ) ) . filter ( x => ! Number . isNaN ( x ) ) . reduce ( ( acc , val ) => acc + val , 0 ) ;
}
const total = getSum ( Object . values ( counts ) ) ;
2023-07-20 19:32:15 +02:00
thisPromptBits . push ( {
2023-08-22 19:34:20 +02:00
oaiStartTokens : ( counts ? . start + counts ? . controlPrompts ) || 0 ,
oaiPromptTokens : getSum ( counts ? . prompt , counts ? . charDescription , counts ? . charPersonality , counts ? . scenario ) || 0 ,
oaiBiasTokens : counts ? . bias || 0 ,
oaiNudgeTokens : counts ? . nudge || 0 ,
oaiJailbreakTokens : counts ? . jailbreak || 0 ,
oaiImpersonateTokens : counts ? . impersonate || 0 ,
oaiExamplesTokens : ( counts ? . dialogueExamples + counts ? . examples ) || 0 ,
oaiConversationTokens : ( counts ? . conversation + counts ? . chatHistory ) || 0 ,
oaiNsfwTokens : counts ? . nsfw || 0 ,
oaiMainTokens : counts ? . main || 0 ,
2023-07-20 19:32:15 +02:00
oaiTotalTokens : total ,
} ) ;
}
2023-08-04 03:22:46 +02:00
function addChatsPreamble ( mesSendString ) {
2023-08-15 03:40:43 +02:00
return main _api === 'novel'
2023-09-09 17:19:01 +02:00
? substituteParams ( nai _settings . preamble ) + '\n' + mesSendString
2023-08-15 14:08:42 +02:00
: mesSendString ;
2023-08-04 03:22:46 +02:00
}
2023-08-03 03:22:06 +02:00
2023-08-04 03:22:46 +02:00
function addChatsSeparator ( mesSendString ) {
2023-08-24 03:04:56 +02:00
if ( power _user . context . chat _start ) {
2023-09-08 12:25:17 +02:00
return substituteParams ( power _user . context . chat _start ) + '\n' + mesSendString ;
2023-07-20 19:32:15 +02:00
}
else {
2023-08-17 21:47:34 +02:00
return mesSendString ;
2023-07-20 19:32:15 +02:00
}
}
2023-08-27 18:24:28 +02:00
function appendZeroDepthAnchor ( force _name2 , zeroDepthAnchor , finalPrompt ) {
2023-09-06 13:07:00 +02:00
const trimBothEnds = ! force _name2 ;
2023-07-20 19:32:15 +02:00
let trimmedPrompt = ( trimBothEnds ? zeroDepthAnchor . trim ( ) : zeroDepthAnchor . trimEnd ( ) ) ;
2023-08-27 18:24:28 +02:00
if ( trimBothEnds && ! finalPrompt . endsWith ( '\n' ) ) {
finalPrompt += '\n' ;
2023-07-20 19:32:15 +02:00
}
2023-08-27 18:24:28 +02:00
finalPrompt += trimmedPrompt ;
2023-07-20 19:32:15 +02:00
2023-09-06 13:07:00 +02:00
if ( force _name2 ) {
2023-08-27 18:24:28 +02:00
finalPrompt += ' ' ;
2023-07-20 19:32:15 +02:00
}
2023-08-27 18:24:28 +02:00
return finalPrompt ;
2023-07-20 19:32:15 +02:00
}
function getMultigenAmount ( ) {
2023-08-22 19:34:20 +02:00
let this _amount _gen = Number ( amount _gen ) ;
2023-07-20 19:32:15 +02:00
if ( tokens _already _generated === 0 ) {
// if the max gen setting is > 50...(
2023-08-22 19:34:20 +02:00
if ( Number ( amount _gen ) >= power _user . multigen _first _chunk ) {
2023-07-20 19:32:15 +02:00
// then only try to make 50 this cycle..
this _amount _gen = power _user . multigen _first _chunk ;
}
else {
// otherwise, make as much as the max amount request.
2023-08-22 19:34:20 +02:00
this _amount _gen = Number ( amount _gen ) ;
2023-07-20 19:32:15 +02:00
}
}
// if we already received some generated text...
else {
// if the remaining tokens to be made is less than next potential cycle count
2023-08-22 19:34:20 +02:00
if ( Number ( amount _gen ) - tokens _already _generated < power _user . multigen _next _chunks ) {
2023-07-20 19:32:15 +02:00
// subtract already generated amount from the desired max gen amount
2023-08-22 19:34:20 +02:00
this _amount _gen = Number ( amount _gen ) - tokens _already _generated ;
2023-07-20 19:32:15 +02:00
}
else {
// otherwise make the standard cycle amount (first 50, and 30 after that)
this _amount _gen = power _user . multigen _next _chunks ;
}
}
return this _amount _gen ;
}
async function DupeChar ( ) {
if ( ! this _chid ) {
toastr . warning ( 'You must first select a character to duplicate!' )
return ;
}
2023-07-22 21:14:16 +02:00
const confirm = await callPopup ( `
< h3 > Are you sure you want to duplicate this character ? < / h 3 >
< span > If you just want to start a new chat with the same character , use "Start new chat" option in the bottom - left options menu . < / s p a n > < b r > < b r > ` ,
'confirm' ,
) ;
2023-07-22 20:50:21 +02:00
if ( ! confirm ) {
console . log ( 'User cancelled duplication' ) ;
return ;
}
2023-07-20 19:32:15 +02:00
const body = { avatar _url : characters [ this _chid ] . avatar } ;
const response = await fetch ( '/dupecharacter' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( body ) ,
} ) ;
if ( response . ok ) {
toastr . success ( "Character Duplicated" ) ;
getCharacters ( ) ;
}
}
function promptItemize ( itemizedPrompts , requestedMesId ) {
console . log ( 'PROMPT ITEMIZE ENTERED' ) ;
var incomingMesId = Number ( requestedMesId ) ;
console . debug ( ` looking for MesId ${ incomingMesId } ` ) ;
var thisPromptSet = undefined ;
for ( var i = 0 ; i < itemizedPrompts . length ; i ++ ) {
console . log ( ` looking for ${ incomingMesId } vs ${ itemizedPrompts [ i ] . mesId } ` )
if ( itemizedPrompts [ i ] . mesId === incomingMesId ) {
console . log ( ` found matching mesID ${ i } ` ) ;
thisPromptSet = i ;
PromptArrayItemForRawPromptDisplay = i ;
console . log ( ` wanting to raw display of ArrayItem: ${ PromptArrayItemForRawPromptDisplay } which is mesID ${ incomingMesId } ` ) ;
console . log ( itemizedPrompts [ thisPromptSet ] ) ;
}
}
if ( thisPromptSet === undefined ) {
console . log ( ` couldnt find the right mesId. looked for ${ incomingMesId } ` ) ;
console . log ( itemizedPrompts ) ;
return null ;
}
//these happen regardless of API
var charDescriptionTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . charDescription ) ;
2023-08-06 06:58:26 +02:00
var charPersonalityTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . charPersonality ) ;
2023-07-20 19:32:15 +02:00
var scenarioTextTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . scenarioText ) ;
2023-08-06 06:58:26 +02:00
var userPersonaStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . userPersona ) ;
var worldInfoStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . worldInfoString ) ;
2023-07-20 19:32:15 +02:00
var allAnchorsTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . allAnchors ) ;
var summarizeStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . summarizeString ) ;
var authorsNoteStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . authorsNoteString ) ;
var smartContextStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . smartContextString ) ;
var afterScenarioAnchorTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . afterScenarioAnchor ) ;
var zeroDepthAnchorTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . zeroDepthAnchor ) ;
var thisPrompt _max _context = itemizedPrompts [ thisPromptSet ] . this _max _context ;
var thisPrompt _padding = itemizedPrompts [ thisPromptSet ] . padding ;
var this _main _api = itemizedPrompts [ thisPromptSet ] . main _api ;
if ( this _main _api == 'openai' ) {
//for OAI API
//console.log('-- Counting OAI Tokens');
//var finalPromptTokens = itemizedPrompts[thisPromptSet].oaiTotalTokens;
2023-08-22 19:34:20 +02:00
var oaiMainTokens = itemizedPrompts [ thisPromptSet ] . oaiMainTokens ;
2023-07-20 19:32:15 +02:00
var oaiStartTokens = itemizedPrompts [ thisPromptSet ] . oaiStartTokens ;
var ActualChatHistoryTokens = itemizedPrompts [ thisPromptSet ] . oaiConversationTokens ;
var examplesStringTokens = itemizedPrompts [ thisPromptSet ] . oaiExamplesTokens ;
2023-09-04 13:31:00 +02:00
var oaiPromptTokens = itemizedPrompts [ thisPromptSet ] . oaiPromptTokens - afterScenarioAnchorTokens + examplesStringTokens ;
2023-07-20 19:32:15 +02:00
var oaiBiasTokens = itemizedPrompts [ thisPromptSet ] . oaiBiasTokens ;
var oaiJailbreakTokens = itemizedPrompts [ thisPromptSet ] . oaiJailbreakTokens ;
var oaiNudgeTokens = itemizedPrompts [ thisPromptSet ] . oaiNudgeTokens ;
var oaiImpersonateTokens = itemizedPrompts [ thisPromptSet ] . oaiImpersonateTokens ;
2023-08-22 19:34:20 +02:00
var oaiNsfwTokens = itemizedPrompts [ thisPromptSet ] . oaiNsfwTokens ;
2023-07-20 19:32:15 +02:00
var finalPromptTokens =
oaiStartTokens +
oaiPromptTokens +
2023-08-22 19:34:20 +02:00
oaiMainTokens +
oaiNsfwTokens +
2023-07-20 19:32:15 +02:00
oaiBiasTokens +
oaiImpersonateTokens +
oaiJailbreakTokens +
oaiNudgeTokens +
ActualChatHistoryTokens +
//charDescriptionTokens +
//charPersonalityTokens +
//allAnchorsTokens +
worldInfoStringTokens +
2023-08-22 19:34:20 +02:00
afterScenarioAnchorTokens ;
2023-07-20 19:32:15 +02:00
// OAI doesn't use padding
thisPrompt _padding = 0 ;
// Max context size - max completion tokens
thisPrompt _max _context = ( oai _settings . openai _max _context - oai _settings . openai _max _tokens ) ;
} else {
//for non-OAI APIs
//console.log('-- Counting non-OAI Tokens');
2023-08-27 18:24:28 +02:00
var finalPromptTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . finalPrompt ) ;
2023-07-20 19:32:15 +02:00
var storyStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . storyString ) - worldInfoStringTokens ;
var examplesStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . examplesString ) ;
var mesSendStringTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . mesSendString )
var ActualChatHistoryTokens = mesSendStringTokens - ( allAnchorsTokens - afterScenarioAnchorTokens ) + power _user . token _padding ;
var instructionTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . instruction ) ;
2023-08-06 06:58:26 +02:00
var promptBiasTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . promptBias ) ;
2023-07-20 19:32:15 +02:00
var totalTokensInPrompt =
storyStringTokens + //chardefs total
worldInfoStringTokens +
examplesStringTokens + // example messages
ActualChatHistoryTokens + //chat history
allAnchorsTokens + // AN and/or legacy anchors
//afterScenarioAnchorTokens + //only counts if AN is set to 'after scenario'
//zeroDepthAnchorTokens + //same as above, even if AN not on 0 depth
promptBiasTokens ; //{{}}
2023-08-27 18:24:28 +02:00
//- thisPrompt_padding; //not sure this way of calculating is correct, but the math results in same value as 'finalPrompt'
2023-07-20 19:32:15 +02:00
}
if ( this _main _api == 'openai' ) {
//console.log('-- applying % on OAI tokens');
var oaiStartTokensPercentage = ( ( oaiStartTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
2023-08-22 19:34:20 +02:00
var storyStringTokensPercentage = ( ( ( afterScenarioAnchorTokens + oaiPromptTokens ) / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
2023-07-20 19:32:15 +02:00
var ActualChatHistoryTokensPercentage = ( ( ActualChatHistoryTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
var promptBiasTokensPercentage = ( ( oaiBiasTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
var worldInfoStringTokensPercentage = ( ( worldInfoStringTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
var allAnchorsTokensPercentage = ( ( allAnchorsTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
var selectedTokenizer = ` tiktoken ( ${ getTokenizerModel ( ) } ) ` ;
2023-08-22 19:34:20 +02:00
var oaiSystemTokens = oaiImpersonateTokens + oaiJailbreakTokens + oaiNudgeTokens + oaiStartTokens + oaiNsfwTokens + oaiMainTokens ;
2023-07-20 19:32:15 +02:00
var oaiSystemTokensPercentage = ( ( oaiSystemTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
} else {
//console.log('-- applying % on non-OAI tokens');
var storyStringTokensPercentage = ( ( storyStringTokens / ( totalTokensInPrompt ) ) * 100 ) . toFixed ( 2 ) ;
var ActualChatHistoryTokensPercentage = ( ( ActualChatHistoryTokens / ( totalTokensInPrompt ) ) * 100 ) . toFixed ( 2 ) ;
var promptBiasTokensPercentage = ( ( promptBiasTokens / ( totalTokensInPrompt ) ) * 100 ) . toFixed ( 2 ) ;
var worldInfoStringTokensPercentage = ( ( worldInfoStringTokens / ( totalTokensInPrompt ) ) * 100 ) . toFixed ( 2 ) ;
var allAnchorsTokensPercentage = ( ( allAnchorsTokens / ( totalTokensInPrompt ) ) * 100 ) . toFixed ( 2 ) ;
var selectedTokenizer = $ ( "#tokenizer" ) . find ( ':selected' ) . text ( ) ;
}
2023-08-22 13:50:43 +02:00
const params = {
selectedTokenizer ,
this _main _api ,
storyStringTokensPercentage ,
worldInfoStringTokensPercentage ,
ActualChatHistoryTokensPercentage ,
allAnchorsTokensPercentage ,
promptBiasTokensPercentage ,
storyStringTokens ,
charDescriptionTokens ,
charPersonalityTokens ,
scenarioTextTokens ,
examplesStringTokens ,
userPersonaStringTokens ,
instructionTokens ,
worldInfoStringTokens ,
ActualChatHistoryTokens ,
allAnchorsTokens ,
summarizeStringTokens ,
authorsNoteStringTokens ,
smartContextStringTokens ,
promptBiasTokens ,
totalTokensInPrompt ,
finalPromptTokens ,
thisPrompt _max _context ,
thisPrompt _padding ,
thisPrompt _actual : thisPrompt _max _context - thisPrompt _padding ,
oaiSystemTokensPercentage ,
oaiStartTokensPercentage ,
oaiSystemTokens ,
oaiStartTokens ,
oaiJailbreakTokens ,
oaiNudgeTokens ,
oaiImpersonateTokens ,
oaiPromptTokens ,
oaiBiasTokens ,
2023-08-22 19:34:20 +02:00
oaiNsfwTokens ,
oaiMainTokens ,
2023-08-22 13:50:43 +02:00
} ;
2023-07-20 19:32:15 +02:00
if ( this _main _api == 'openai' ) {
2023-08-22 13:50:43 +02:00
callPopup ( renderTemplate ( 'itemizationChat' , params ) , 'text' ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-08-22 13:50:43 +02:00
callPopup ( renderTemplate ( 'itemizationText' , params ) , 'text' ) ;
2023-07-20 19:32:15 +02:00
}
}
function setInContextMessages ( lastmsg , type ) {
$ ( "#chat .mes" ) . removeClass ( 'lastInContext' ) ;
if ( type === 'swipe' || type === 'regenerate' || type === 'continue' ) {
lastmsg ++ ;
}
2023-09-11 00:07:45 +02:00
const lastMessageBlock = $ ( '#chat .mes:not([is_system="true"])' ) . eq ( - lastmsg ) ;
lastMessageBlock . addClass ( 'lastInContext' ) ;
if ( lastMessageBlock . length === 0 ) {
const firstMessageId = getFirstDisplayedMessageId ( ) ;
$ ( ` #chat .mes[mesid=" ${ firstMessageId } " ` ) . addClass ( 'lastInContext' ) ;
}
2023-07-20 19:32:15 +02:00
}
function getGenerateUrl ( ) {
let generate _url = '' ;
if ( main _api == 'kobold' ) {
generate _url = '/generate' ;
} else if ( main _api == 'textgenerationwebui' ) {
generate _url = '/generate_textgenerationwebui' ;
} else if ( main _api == 'novel' ) {
generate _url = '/generate_novelai' ;
}
return generate _url ;
}
function shouldContinueMultigen ( getMessage , isImpersonate , isInstruct ) {
if ( isInstruct && power _user . instruct . stop _sequence ) {
if ( message _already _generated . indexOf ( power _user . instruct . stop _sequence ) !== - 1 ) {
return false ;
}
}
// stopping name string
2023-09-06 13:07:00 +02:00
const nameString = isImpersonate ? ` ${ name2 } : ` : ` ${ name1 } : ` ;
2023-07-20 19:32:15 +02:00
// if there is no 'You:' in the response msg
const doesNotContainName = message _already _generated . indexOf ( nameString ) === - 1 ;
//if there is no <endoftext> stamp in the response msg
const isNotEndOfText = message _already _generated . indexOf ( '<|endoftext|>' ) === - 1 ;
//if the gen'd msg is less than the max response length..
2023-09-06 13:07:00 +02:00
const notReachedMax = tokens _already _generated < Number ( amount _gen ) ;
2023-07-20 19:32:15 +02:00
//if we actually have gen'd text at all...
const msgHasText = getMessage . length > 0 ;
return doesNotContainName && isNotEndOfText && notReachedMax && msgHasText ;
}
function extractNameFromMessage ( getMessage , force _name2 , isImpersonate ) {
const nameToTrim = isImpersonate ? name1 : name2 ;
let this _mes _is _name = true ;
if ( getMessage . startsWith ( nameToTrim + ":" ) ) {
getMessage = getMessage . replace ( nameToTrim + ':' , '' ) ;
getMessage = getMessage . trimStart ( ) ;
} else {
this _mes _is _name = false ;
}
if ( force _name2 || power _user . instruct . enabled )
this _mes _is _name = true ;
if ( isImpersonate ) {
getMessage = getMessage . trim ( ) ;
}
return { this _mes _is _name , getMessage } ;
}
function throwCircuitBreakerError ( ) {
callPopup ( ` Could not extract reply in ${ MAX _GENERATION _LOOPS } attempts. Try generating again ` , 'text' ) ;
generate _loop _counter = 0 ;
$ ( "#send_textarea" ) . removeAttr ( 'disabled' ) ;
is _send _press = false ;
activateSendButtons ( ) ;
setGenerationProgress ( 0 ) ;
showSwipeButtons ( ) ;
throw new Error ( 'Generate circuit breaker interruption' ) ;
}
function extractTitleFromData ( data ) {
if ( main _api == 'koboldhorde' ) {
return data . workerName ;
}
return undefined ;
}
function extractMessageFromData ( data ) {
switch ( main _api ) {
case 'kobold' :
return data . results [ 0 ] . text ;
case 'koboldhorde' :
return data . text ;
case 'textgenerationwebui' :
return data . results [ 0 ] . text ;
case 'novel' :
return data . output ;
case 'openai' :
return data ;
default :
return ''
}
}
function cleanUpMessage ( getMessage , isImpersonate , isContinue , displayIncompleteSentences = false ) {
// Add the prompt bias before anything else
if (
power _user . user _prompt _bias &&
! isImpersonate &&
! isContinue &&
power _user . user _prompt _bias . length !== 0
) {
getMessage = substituteParams ( power _user . user _prompt _bias ) + getMessage ;
}
// Regex uses vars, so add before formatting
getMessage = getRegexedString ( getMessage , isImpersonate ? regex _placement . USER _INPUT : regex _placement . AI _OUTPUT ) ;
if ( ! displayIncompleteSentences && power _user . trim _sentences ) {
2023-09-09 16:31:27 +02:00
getMessage = trimToEndSentence ( getMessage , power _user . include _newline ) ;
2023-07-20 19:32:15 +02:00
}
if ( power _user . collapse _newlines ) {
getMessage = collapseNewlines ( getMessage ) ;
}
if ( power _user . trim _spaces ) {
getMessage = getMessage . trim ( ) ;
}
// trailing invisible whitespace before every newlines, on a multiline string
// "trailing whitespace on newlines \nevery line of the string \n?sample text" ->
// "trailing whitespace on newlines\nevery line of the string\nsample text"
getMessage = getMessage . replace ( /[^\S\r\n]+$/gm , "" ) ;
let nameToTrim = isImpersonate ? name2 : name1 ;
if ( isImpersonate ) {
nameToTrim = power _user . allow _name2 _display ? '' : name2 ;
}
else {
nameToTrim = power _user . allow _name1 _display ? '' : name1 ;
}
if ( nameToTrim && getMessage . indexOf ( ` ${ nameToTrim } : ` ) == 0 ) {
getMessage = getMessage . substr ( 0 , getMessage . indexOf ( ` ${ nameToTrim } : ` ) ) ;
}
if ( nameToTrim && getMessage . indexOf ( ` \n ${ nameToTrim } : ` ) > 0 ) {
getMessage = getMessage . substr ( 0 , getMessage . indexOf ( ` \n ${ nameToTrim } : ` ) ) ;
}
if ( getMessage . indexOf ( '<|endoftext|>' ) != - 1 ) {
getMessage = getMessage . substr ( 0 , getMessage . indexOf ( '<|endoftext|>' ) ) ;
}
const isInstruct = power _user . instruct . enabled && main _api !== 'openai' ;
if ( isInstruct && power _user . instruct . stop _sequence ) {
if ( getMessage . indexOf ( power _user . instruct . stop _sequence ) != - 1 ) {
getMessage = getMessage . substring ( 0 , getMessage . indexOf ( power _user . instruct . stop _sequence ) ) ;
}
}
2023-09-04 23:29:49 +02:00
// Hana: Only use the first sequence (should be <|model|>)
// of the prompt before <|user|> (as KoboldAI Lite does it).
if ( isInstruct && power _user . instruct . input _sequence ) {
if ( getMessage . indexOf ( power _user . instruct . input _sequence ) != - 1 ) {
getMessage = getMessage . substring ( 0 , getMessage . indexOf ( power _user . instruct . input _sequence ) ) ;
}
}
2023-07-20 19:32:15 +02:00
if ( isInstruct && power _user . instruct . input _sequence && isImpersonate ) {
2023-08-18 23:28:11 +02:00
//getMessage = getMessage.replaceAll(power_user.instruct.input_sequence, '');
power _user . instruct . input _sequence . split ( '\n' )
. filter ( line => line . trim ( ) !== '' )
. forEach ( line => {
getMessage = getMessage . replaceAll ( line , '' ) ;
} ) ;
2023-07-20 19:32:15 +02:00
}
if ( isInstruct && power _user . instruct . output _sequence && ! isImpersonate ) {
2023-08-18 23:28:11 +02:00
//getMessage = getMessage.replaceAll(power_user.instruct.output_sequence, '');
power _user . instruct . output _sequence . split ( '\n' )
. filter ( line => line . trim ( ) !== '' )
. forEach ( line => {
getMessage = getMessage . replaceAll ( line , '' ) ;
} ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-18 21:18:27 +02:00
if ( isInstruct && power _user . instruct . last _output _sequence && ! isImpersonate ) {
2023-08-18 23:28:11 +02:00
//getMessage = getMessage.replaceAll(power_user.instruct.last_output_sequence, '');
power _user . instruct . last _output _sequence . split ( '\n' )
. filter ( line => line . trim ( ) !== '' )
. forEach ( line => {
getMessage = getMessage . replaceAll ( line , '' ) ;
} ) ;
2023-07-20 19:32:15 +02:00
}
// clean-up group message from excessive generations
if ( selected _group ) {
getMessage = cleanGroupMessage ( getMessage ) ;
}
2023-08-06 23:31:26 +02:00
if ( ! power _user . allow _name2 _display ) {
2023-08-07 01:20:53 +02:00
getMessage = getMessage . replace ( new RegExp ( ` (^| \n ) ${ name2 } : ` , 'g' ) , "$1" ) ;
2023-08-06 23:31:26 +02:00
}
2023-07-20 19:32:15 +02:00
if ( isImpersonate ) {
getMessage = getMessage . trim ( ) ;
}
2023-09-06 13:19:29 +02:00
const stoppingStrings = getStoppingStrings ( isImpersonate ) ;
2023-07-20 19:32:15 +02:00
for ( const stoppingString of stoppingStrings ) {
if ( stoppingString . length ) {
for ( let j = stoppingString . length - 1 ; j > 0 ; j -- ) {
if ( getMessage . slice ( - j ) === stoppingString . slice ( 0 , j ) ) {
getMessage = getMessage . slice ( 0 , - j ) ;
break ;
}
}
}
}
if ( power _user . auto _fix _generated _markdown ) {
2023-08-31 13:39:31 +02:00
getMessage = fixMarkdown ( getMessage , false ) ;
2023-07-20 19:32:15 +02:00
}
return getMessage ;
}
2023-08-22 16:46:37 +02:00
async function saveReply ( type , getMessage , this _mes _is _name , title ) {
2023-07-20 19:32:15 +02:00
if ( type != 'append' && type != 'continue' && type != 'appendFinal' && chat . length && ( chat [ chat . length - 1 ] [ 'swipe_id' ] === undefined ||
chat [ chat . length - 1 ] [ 'is_user' ] ) ) {
type = 'normal' ;
}
2023-08-24 14:13:04 +02:00
if ( chat . length && typeof chat [ chat . length - 1 ] [ 'extra' ] !== 'object' ) {
chat [ chat . length - 1 ] [ 'extra' ] = { } ;
}
2023-07-20 19:32:15 +02:00
let oldMessage = ''
const generationFinished = new Date ( ) ;
const img = extractImageFromMessage ( getMessage ) ;
getMessage = img . getMessage ;
if ( type === 'swipe' ) {
oldMessage = chat [ chat . length - 1 ] [ 'mes' ] ;
chat [ chat . length - 1 ] [ 'swipes' ] . length ++ ;
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] === chat [ chat . length - 1 ] [ 'swipes' ] . length - 1 ) {
chat [ chat . length - 1 ] [ 'title' ] = title ;
chat [ chat . length - 1 ] [ 'mes' ] = getMessage ;
chat [ chat . length - 1 ] [ 'gen_started' ] = generation _started ;
chat [ chat . length - 1 ] [ 'gen_finished' ] = generationFinished ;
chat [ chat . length - 1 ] [ 'send_date' ] = getMessageTimeStamp ( ) ;
2023-08-16 12:51:38 +02:00
chat [ chat . length - 1 ] [ 'extra' ] [ 'api' ] = getGeneratingApi ( ) ;
2023-07-20 19:32:15 +02:00
chat [ chat . length - 1 ] [ 'extra' ] [ 'model' ] = getGeneratingModel ( ) ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = getTokenCount ( chat [ chat . length - 1 ] [ 'mes' ] , 0 ) ;
}
2023-08-22 16:46:37 +02:00
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
2023-08-22 21:45:12 +02:00
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
} else {
chat [ chat . length - 1 ] [ 'mes' ] = getMessage ;
}
} else if ( type === 'append' || type === 'continue' ) {
console . debug ( "Trying to append." )
oldMessage = chat [ chat . length - 1 ] [ 'mes' ] ;
chat [ chat . length - 1 ] [ 'title' ] = title ;
chat [ chat . length - 1 ] [ 'mes' ] += getMessage ;
chat [ chat . length - 1 ] [ 'gen_started' ] = generation _started ;
chat [ chat . length - 1 ] [ 'gen_finished' ] = generationFinished ;
chat [ chat . length - 1 ] [ 'send_date' ] = getMessageTimeStamp ( ) ;
2023-08-16 12:51:38 +02:00
chat [ chat . length - 1 ] [ "extra" ] [ "api" ] = getGeneratingApi ( ) ;
2023-07-20 19:32:15 +02:00
chat [ chat . length - 1 ] [ "extra" ] [ "model" ] = getGeneratingModel ( ) ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = getTokenCount ( chat [ chat . length - 1 ] [ 'mes' ] , 0 ) ;
}
2023-08-22 16:46:37 +02:00
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
2023-08-22 21:45:12 +02:00
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
} else if ( type === 'appendFinal' ) {
oldMessage = chat [ chat . length - 1 ] [ 'mes' ] ;
console . debug ( "Trying to appendFinal." )
chat [ chat . length - 1 ] [ 'title' ] = title ;
chat [ chat . length - 1 ] [ 'mes' ] = getMessage ;
chat [ chat . length - 1 ] [ 'gen_started' ] = generation _started ;
chat [ chat . length - 1 ] [ 'gen_finished' ] = generationFinished ;
chat [ chat . length - 1 ] [ 'send_date' ] = getMessageTimeStamp ( ) ;
2023-08-16 12:51:38 +02:00
chat [ chat . length - 1 ] [ "extra" ] [ "api" ] = getGeneratingApi ( ) ;
2023-07-20 19:32:15 +02:00
chat [ chat . length - 1 ] [ "extra" ] [ "model" ] = getGeneratingModel ( ) ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = getTokenCount ( chat [ chat . length - 1 ] [ 'mes' ] , 0 ) ;
}
2023-08-22 16:46:37 +02:00
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
2023-08-22 21:45:12 +02:00
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
} else {
console . debug ( 'entering chat update routine for non-swipe post' ) ;
chat [ chat . length ] = { } ;
chat [ chat . length - 1 ] [ 'extra' ] = { } ;
chat [ chat . length - 1 ] [ 'name' ] = name2 ;
chat [ chat . length - 1 ] [ 'is_user' ] = false ;
chat [ chat . length - 1 ] [ 'is_name' ] = this _mes _is _name ;
chat [ chat . length - 1 ] [ 'send_date' ] = getMessageTimeStamp ( ) ;
2023-08-16 12:51:38 +02:00
chat [ chat . length - 1 ] [ "extra" ] [ "api" ] = getGeneratingApi ( ) ;
2023-07-20 19:32:15 +02:00
chat [ chat . length - 1 ] [ "extra" ] [ "model" ] = getGeneratingModel ( ) ;
if ( power _user . trim _spaces ) {
getMessage = getMessage . trim ( ) ;
}
chat [ chat . length - 1 ] [ 'mes' ] = getMessage ;
chat [ chat . length - 1 ] [ 'title' ] = title ;
chat [ chat . length - 1 ] [ 'gen_started' ] = generation _started ;
chat [ chat . length - 1 ] [ 'gen_finished' ] = generationFinished ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = getTokenCount ( chat [ chat . length - 1 ] [ 'mes' ] , 0 ) ;
}
2023-07-20 19:32:15 +02:00
if ( selected _group ) {
console . debug ( 'entering chat update for groups' ) ;
let avatarImg = 'img/ai4.png' ;
if ( characters [ this _chid ] . avatar != 'none' ) {
avatarImg = getThumbnailUrl ( 'avatar' , characters [ this _chid ] . avatar ) ;
}
chat [ chat . length - 1 ] [ 'is_name' ] = true ;
chat [ chat . length - 1 ] [ 'force_avatar' ] = avatarImg ;
chat [ chat . length - 1 ] [ 'original_avatar' ] = characters [ this _chid ] . avatar ;
chat [ chat . length - 1 ] [ 'extra' ] [ 'gen_id' ] = group _generation _id ;
}
saveImageToMessage ( img , chat [ chat . length - 1 ] ) ;
2023-08-22 16:46:37 +02:00
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
addOneMessage ( chat [ chat . length - 1 ] ) ;
2023-08-22 21:45:12 +02:00
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
}
const item = chat [ chat . length - 1 ] ;
if ( item [ "swipe_info" ] === undefined ) {
item [ "swipe_info" ] = [ ] ;
}
if ( item [ "swipe_id" ] !== undefined ) {
2023-08-25 00:27:27 +02:00
const swipeId = item [ "swipe_id" ] ;
item [ "swipes" ] [ swipeId ] = item [ "mes" ] ;
item [ "swipe_info" ] [ swipeId ] = {
2023-07-20 19:32:15 +02:00
send _date : item [ "send_date" ] ,
gen _started : item [ "gen_started" ] ,
gen _finished : item [ "gen_finished" ] ,
extra : JSON . parse ( JSON . stringify ( item [ "extra" ] ) ) ,
} ;
} else {
item [ "swipe_id" ] = 0 ;
item [ "swipes" ] = [ ] ;
item [ "swipes" ] [ 0 ] = chat [ chat . length - 1 ] [ "mes" ] ;
item [ "swipe_info" ] [ 0 ] = {
send _date : chat [ chat . length - 1 ] [ "send_date" ] ,
gen _started : chat [ chat . length - 1 ] [ "gen_started" ] ,
gen _finished : chat [ chat . length - 1 ] [ "gen_finished" ] ,
extra : JSON . parse ( JSON . stringify ( chat [ chat . length - 1 ] [ "extra" ] ) ) ,
} ;
}
statMesProcess ( chat [ chat . length - 1 ] , type , characters , this _chid , oldMessage ) ;
return { type , getMessage } ;
}
function saveImageToMessage ( img , mes ) {
if ( mes && img . image ) {
if ( typeof mes . extra !== 'object' ) {
mes . extra = { } ;
}
mes . extra . image = img . image ;
mes . extra . title = img . title ;
}
}
2023-08-16 12:51:38 +02:00
function getGeneratingApi ( ) {
switch ( main _api ) {
case 'openai' :
return oai _settings . chat _completion _source || 'openai' ;
default :
return main _api ;
}
}
2023-07-20 19:32:15 +02:00
function getGeneratingModel ( mes ) {
let model = '' ;
switch ( main _api ) {
case 'kobold' :
model = online _status ;
break ;
case 'novel' :
model = nai _settings . model _novel ;
break ;
case 'openai' :
model = getChatCompletionModel ( ) ;
break ;
case 'textgenerationwebui' :
model = online _status ;
break ;
case 'koboldhorde' :
model = kobold _horde _model ;
break ;
}
return model
}
function extractImageFromMessage ( getMessage ) {
const regex = /<img src="(.*?)".*?alt="(.*?)".*?>/g ;
const results = regex . exec ( getMessage ) ;
const image = results ? results [ 1 ] : '' ;
const title = results ? results [ 2 ] : '' ;
getMessage = getMessage . replace ( regex , '' ) ;
return { getMessage , image , title } ;
}
export function isMultigenEnabled ( ) {
return power _user . multigen && ( main _api == 'textgenerationwebui' || main _api == 'kobold' || main _api == 'koboldhorde' || main _api == 'novel' ) ;
}
export function activateSendButtons ( ) {
is _send _press = false ;
2023-08-24 00:37:44 +02:00
$ ( "#send_but" ) . removeClass ( "displayNone" ) ;
$ ( "#mes_continue" ) . removeClass ( "displayNone" ) ;
2023-07-20 19:32:15 +02:00
$ ( "#send_textarea" ) . attr ( "disabled" , false ) ;
$ ( '.mes_buttons:last' ) . show ( ) ;
hideStopButton ( ) ;
}
export function deactivateSendButtons ( ) {
2023-08-24 00:37:44 +02:00
$ ( "#send_but" ) . addClass ( "displayNone" ) ;
$ ( "#mes_continue" ) . addClass ( "displayNone" ) ;
2023-07-20 19:32:15 +02:00
showStopButton ( ) ;
}
function resetChatState ( ) {
//unsets expected chid before reloading (related to getCharacters/printCharacters from using old arrays)
this _chid = "invalid-safety-id" ;
// replaces deleted charcter name with system user since it will be displayed next.
name2 = systemUserName ;
// sets up system user to tell user about having deleted a character
chat = [ ... safetychat ] ;
// resets chat metadata
chat _metadata = { } ;
// resets the characters array, forcing getcharacters to reset
characters . length = 0 ;
}
export function setMenuType ( value ) {
menu _type = value ;
}
2023-08-24 20:23:35 +02:00
export function setExternalAbortController ( controller ) {
abortController = controller ;
}
2023-07-20 19:32:15 +02:00
function setCharacterId ( value ) {
this _chid = value ;
}
function setCharacterName ( value ) {
name2 = value ;
}
function setOnlineStatus ( value ) {
online _status = value ;
}
function setEditedMessageId ( value ) {
this _edit _mes _id = value ;
}
function setSendButtonState ( value ) {
is _send _press = value ;
}
function resultCheckStatusNovel ( ) {
is _api _button _press _novel = false ;
checkOnlineStatus ( ) ;
$ ( "#api_loading_novel" ) . css ( "display" , "none" ) ;
$ ( "#api_button_novel" ) . css ( "display" , "inline-block" ) ;
}
async function renameCharacter ( ) {
const oldAvatar = characters [ this _chid ] . avatar ;
const newValue = await callPopup ( '<h3>New name:</h3>' , 'input' , characters [ this _chid ] . name ) ;
if ( newValue && newValue !== characters [ this _chid ] . name ) {
const body = JSON . stringify ( { avatar _url : oldAvatar , new _name : newValue } ) ;
const response = await fetch ( '/renamecharacter' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body ,
} ) ;
try {
if ( response . ok ) {
const data = await response . json ( ) ;
const newAvatar = data . avatar ;
// Replace tags list
renameTagKey ( oldAvatar , newAvatar ) ;
// Reload characters list
await getCharacters ( ) ;
// Find newly renamed character
const newChId = characters . findIndex ( c => c . avatar == data . avatar ) ;
if ( newChId !== - 1 ) {
// Select the character after the renaming
this _chid = - 1 ;
2023-08-18 22:13:15 +02:00
selectCharacterById ( String ( newChId ) ) ;
2023-07-20 19:32:15 +02:00
// Async delay to update UI
await delay ( 1 ) ;
if ( this _chid === - 1 ) {
throw new Error ( 'New character not selected' ) ;
}
// Also rename as a group member
await renameGroupMember ( oldAvatar , newAvatar , newValue ) ;
const renamePastChatsConfirm = await callPopup ( ` <h3>Character renamed!</h3>
< p > Past chats will still contain the old character name . Would you like to update the character name in previous chats as well ? < / p >
< i > < b > Sprites folder ( if any ) should be renamed manually . < /b></i > ` , 'confirm');
if ( renamePastChatsConfirm ) {
await renamePastChats ( newAvatar , newValue ) ;
await reloadCurrentChat ( ) ;
toastr . success ( 'Character renamed and past chats updated!' ) ;
}
}
else {
throw new Error ( 'Newly renamed character was lost?' ) ;
}
}
else {
throw new Error ( 'Could not rename the character' ) ;
}
}
catch {
// Reloading to prevent data corruption
await callPopup ( 'Something went wrong. The page will be reloaded.' , 'text' ) ;
location . reload ( ) ;
}
}
}
async function renamePastChats ( newAvatar , newValue ) {
const pastChats = await getPastCharacterChats ( ) ;
for ( const { file _name } of pastChats ) {
try {
const fileNameWithoutExtension = file _name . replace ( '.jsonl' , '' ) ;
const getChatResponse = await fetch ( '/getchat' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
ch _name : newValue ,
file _name : fileNameWithoutExtension ,
avatar _url : newAvatar ,
} ) ,
cache : 'no-cache' ,
} ) ;
if ( getChatResponse . ok ) {
const currentChat = await getChatResponse . json ( ) ;
for ( const message of currentChat ) {
if ( message . is _user || message . is _system || message . extra ? . type == system _message _types . NARRATOR ) {
continue ;
}
if ( message . name !== undefined ) {
message . name = newValue ;
}
}
const saveChatResponse = await fetch ( '/savechat' , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
ch _name : newValue ,
file _name : fileNameWithoutExtension ,
chat : currentChat ,
avatar _url : newAvatar ,
} ) ,
cache : 'no-cache' ,
} ) ;
if ( ! saveChatResponse . ok ) {
throw new Error ( 'Could not save chat' ) ;
}
}
} catch ( error ) {
toastr . error ( ` Past chat could not be updated: ${ file _name } ` ) ;
console . error ( error ) ;
}
}
}
2023-08-23 20:32:38 +02:00
function saveChatDebounced ( ) {
const chid = this _chid ;
const selectedGroup = selected _group ;
if ( chatSaveTimeout ) {
console . debug ( 'Clearing chat save timeout' ) ;
clearTimeout ( chatSaveTimeout ) ;
}
chatSaveTimeout = setTimeout ( async ( ) => {
if ( selectedGroup !== selected _group ) {
console . warn ( 'Chat save timeout triggered, but group changed. Aborting.' ) ;
return ;
}
if ( chid !== this _chid ) {
console . warn ( 'Chat save timeout triggered, but chid changed. Aborting.' ) ;
return ;
}
console . debug ( 'Chat save timeout triggered' ) ;
await saveChatConditional ( ) ;
console . debug ( 'Chat saved' ) ;
} , 1000 ) ;
}
2023-07-20 19:32:15 +02:00
async function saveChat ( chat _name , withMetadata , mesId ) {
const metadata = { ... chat _metadata , ... ( withMetadata || { } ) } ;
let file _name = chat _name ? ? characters [ this _chid ] . chat ;
characters [ this _chid ] [ 'date_last_chat' ] = Date . now ( ) ;
chat . forEach ( function ( item , i ) {
if ( item [ "is_group" ] ) {
toastr . error ( 'Trying to save group chat with regular saveChat function. Aborting to prevent corruption.' ) ;
throw new Error ( 'Group chat saved from saveChat' ) ;
}
/ *
if ( item . is _user ) {
//var str = item.mes.replace(`${name1}:`, `${name1}:`);
//chat[i].mes = str;
//chat[i].name = name1;
} else if ( i !== chat . length - 1 && chat [ i ] . swipe _id !== undefined ) {
// delete chat[i].swipes;
// delete chat[i].swipe_id;
}
* /
} ) ;
const trimmed _chat = ( mesId !== undefined && mesId >= 0 && mesId < chat . length )
? chat . slice ( 0 , parseInt ( mesId ) + 1 )
: chat ;
var save _chat = [
{
user _name : name1 ,
character _name : name2 ,
create _date : chat _create _date ,
chat _metadata : metadata ,
} ,
... trimmed _chat ,
] ;
return jQuery . ajax ( {
type : "POST" ,
url : "/savechat" ,
data : JSON . stringify ( {
ch _name : characters [ this _chid ] . name ,
file _name : file _name ,
chat : save _chat ,
avatar _url : characters [ this _chid ] . avatar ,
} ) ,
beforeSend : function ( ) {
} ,
cache : false ,
dataType : "json" ,
contentType : "application/json" ,
success : function ( data ) { } ,
error : function ( jqXHR , exception ) {
console . log ( exception ) ;
console . log ( jqXHR ) ;
} ,
} ) ;
}
async function read _avatar _load ( input ) {
if ( input . files && input . files [ 0 ] ) {
if ( selected _button == "create" ) {
create _save . avatar = input . files ;
}
const e = await new Promise ( ( resolve , reject ) => {
const reader = new FileReader ( ) ;
reader . onload = resolve ;
reader . onerror = reject ;
reader . readAsDataURL ( input . files [ 0 ] ) ;
} )
$ ( '#dialogue_popup' ) . addClass ( 'large_dialogue_popup wide_dialogue_popup' ) ;
const croppedImage = await callPopup ( getCropPopup ( e . target . result ) , 'avatarToCrop' ) ;
if ( ! croppedImage ) {
return ;
}
$ ( "#avatar_load_preview" ) . attr ( "src" , croppedImage || e . target . result ) ;
if ( menu _type == "create" ) {
return ;
}
2023-08-08 21:08:41 +02:00
await createOrEditCharacter ( ) ;
await delay ( durationSaveEdit ) ;
2023-07-20 19:32:15 +02:00
const formData = new FormData ( $ ( "#form_create" ) . get ( 0 ) ) ;
2023-08-08 21:08:41 +02:00
await fetch ( getThumbnailUrl ( 'avatar' , formData . get ( 'avatar_url' ) ) , {
method : 'GET' ,
cache : 'no-cache' ,
headers : {
'pragma' : 'no-cache' ,
'cache-control' : 'no-cache' ,
}
} ) ;
2023-07-20 19:32:15 +02:00
$ ( ".mes" ) . each ( async function ( ) {
2023-09-12 23:51:21 +02:00
const nameMatch = $ ( this ) . attr ( "ch_name" ) == formData . get ( 'ch_name' ) ;
if ( $ ( this ) . attr ( "is_system" ) == 'true' && ! nameMatch ) {
2023-07-20 19:32:15 +02:00
return ;
}
if ( $ ( this ) . attr ( "is_user" ) == 'true' ) {
return ;
}
2023-09-12 23:51:21 +02:00
if ( nameMatch ) {
2023-07-20 19:32:15 +02:00
const previewSrc = $ ( "#avatar_load_preview" ) . attr ( "src" ) ;
const avatar = $ ( this ) . find ( ".avatar img" ) ;
avatar . attr ( 'src' , default _avatar ) ;
await delay ( 1 ) ;
avatar . attr ( 'src' , previewSrc ) ;
}
} ) ;
console . log ( 'Avatar refreshed' ) ;
}
}
export function getCropPopup ( src ) {
2023-08-07 18:49:07 +02:00
return ` <h3>Set the crop position of the avatar image and click Accept to confirm.</h3>
2023-07-20 19:32:15 +02:00
< div id = 'avatarCropWrap' >
< img id = 'avatarToCrop' src = '${src}' >
< / d i v > ` ;
}
function getThumbnailUrl ( type , file ) {
return ` /thumbnail?type= ${ type } &file= ${ encodeURIComponent ( file ) } ` ;
}
async function getChat ( ) {
//console.log('/getchat -- entered for -- ' + characters[this_chid].name);
try {
const response = await $ . ajax ( {
type : 'POST' ,
url : '/getchat' ,
data : JSON . stringify ( {
ch _name : characters [ this _chid ] . name ,
file _name : characters [ this _chid ] . chat ,
avatar _url : characters [ this _chid ] . avatar
} ) ,
dataType : 'json' ,
contentType : 'application/json' ,
} ) ;
if ( response [ 0 ] !== undefined ) {
chat . push ( ... response ) ;
chat _create _date = chat [ 0 ] [ 'create_date' ] ;
chat _metadata = chat [ 0 ] [ 'chat_metadata' ] ? ? { } ;
chat . shift ( ) ;
} else {
chat _create _date = humanizedDateTime ( ) ;
}
await getChatResult ( ) ;
2023-08-18 15:45:40 +02:00
eventSource . emit ( 'chatLoaded' , { detail : { id : this _chid , character : characters [ this _chid ] } } ) ;
2023-07-20 19:32:15 +02:00
setTimeout ( function ( ) {
$ ( '#send_textarea' ) . click ( ) ;
$ ( '#send_textarea' ) . focus ( ) ;
} , 200 ) ;
} catch ( error ) {
await getChatResult ( ) ;
console . log ( error ) ;
}
}
async function getChatResult ( ) {
name2 = characters [ this _chid ] . name ;
if ( chat . length === 0 ) {
2023-08-23 20:32:38 +02:00
const message = getFirstMessage ( ) ;
chat . push ( message ) ;
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-07-20 19:32:15 +02:00
select _selected _character ( this _chid ) ;
await eventSource . emit ( event _types . CHAT _CHANGED , ( getCurrentChatId ( ) ) ) ;
if ( chat . length === 1 ) {
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
2023-08-22 21:45:12 +02:00
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
2023-07-20 19:32:15 +02:00
}
}
2023-08-23 20:32:38 +02:00
function getFirstMessage ( ) {
const firstMes = characters [ this _chid ] . first _mes || default _ch _mes ;
const alternateGreetings = characters [ this _chid ] ? . data ? . alternate _greetings ;
const message = {
name : name2 ,
is _user : false ,
2023-08-24 14:13:04 +02:00
is _system : false ,
2023-08-23 20:32:38 +02:00
is _name : true ,
send _date : getMessageTimeStamp ( ) ,
mes : getRegexedString ( firstMes , regex _placement . AI _OUTPUT ) ,
2023-08-24 14:13:04 +02:00
extra : { } ,
2023-08-23 20:32:38 +02:00
} ;
if ( Array . isArray ( alternateGreetings ) && alternateGreetings . length > 0 ) {
2023-08-24 10:04:46 +02:00
const swipes = [ message . mes , ... ( alternateGreetings . map ( greeting => substituteParams ( getRegexedString ( greeting , regex _placement . AI _OUTPUT ) ) ) ) ] ;
2023-08-23 20:32:38 +02:00
message [ 'swipe_id' ] = 0 ;
2023-08-24 10:04:46 +02:00
message [ 'swipes' ] = swipes ;
2023-08-23 20:32:38 +02:00
message [ 'swipe_info' ] = [ ] ;
}
return message ;
}
2023-07-20 19:32:15 +02:00
async function openCharacterChat ( file _name ) {
characters [ this _chid ] [ "chat" ] = file _name ;
clearChat ( ) ;
chat . length = 0 ;
chat _metadata = { } ;
await getChat ( ) ;
$ ( "#selected_chat_pole" ) . val ( file _name ) ;
2023-08-19 14:58:17 +02:00
await createOrEditCharacter ( ) ;
2023-07-20 19:32:15 +02:00
}
////////// OPTIMZED MAIN API CHANGE FUNCTION ////////////
function changeMainAPI ( ) {
const selectedVal = $ ( "#main_api" ) . val ( ) ;
//console.log(selectedVal);
const apiElements = {
"koboldhorde" : {
apiSettings : $ ( "#kobold_api-settings" ) ,
apiConnector : $ ( "#kobold_horde" ) ,
apiPresets : $ ( '#kobold_api-presets' ) ,
apiRanges : $ ( "#range_block" ) ,
maxContextElem : $ ( "#max_context_block" ) ,
amountGenElem : $ ( "#amount_gen_block" ) ,
} ,
"kobold" : {
apiSettings : $ ( "#kobold_api-settings" ) ,
apiConnector : $ ( "#kobold_api" ) ,
apiPresets : $ ( '#kobold_api-presets' ) ,
apiRanges : $ ( "#range_block" ) ,
maxContextElem : $ ( "#max_context_block" ) ,
amountGenElem : $ ( "#amount_gen_block" ) ,
} ,
"textgenerationwebui" : {
apiSettings : $ ( "#textgenerationwebui_api-settings" ) ,
apiConnector : $ ( "#textgenerationwebui_api" ) ,
apiPresets : $ ( '#textgenerationwebui_api-presets' ) ,
apiRanges : $ ( "#range_block_textgenerationwebui" ) ,
maxContextElem : $ ( "#max_context_block" ) ,
amountGenElem : $ ( "#amount_gen_block" ) ,
} ,
"novel" : {
apiSettings : $ ( "#novel_api-settings" ) ,
apiConnector : $ ( "#novel_api" ) ,
apiPresets : $ ( '#novel_api-presets' ) ,
apiRanges : $ ( "#range_block_novel" ) ,
maxContextElem : $ ( "#max_context_block" ) ,
amountGenElem : $ ( "#amount_gen_block" ) ,
} ,
"openai" : {
apiSettings : $ ( "#openai_settings" ) ,
apiConnector : $ ( "#openai_api" ) ,
apiPresets : $ ( '#openai_api-presets' ) ,
apiRanges : $ ( "#range_block_openai" ) ,
maxContextElem : $ ( "#max_context_block" ) ,
amountGenElem : $ ( "#amount_gen_block" ) ,
}
} ;
//console.log('--- apiElements--- ');
//console.log(apiElements);
//first, disable everything so the old elements stop showing
for ( const apiName in apiElements ) {
const apiObj = apiElements [ apiName ] ;
//do not hide items to then proceed to immediately show them.
if ( selectedVal === apiName ) {
continue ;
}
apiObj . apiSettings . css ( "display" , "none" ) ;
apiObj . apiConnector . css ( "display" , "none" ) ;
apiObj . apiRanges . css ( "display" , "none" ) ;
apiObj . apiPresets . css ( "display" , "none" ) ;
}
//then, find and enable the active item.
//This is split out of the loop so that different apis can share settings divs
let activeItem = apiElements [ selectedVal ] ;
activeItem . apiSettings . css ( "display" , "block" ) ;
activeItem . apiConnector . css ( "display" , "block" ) ;
activeItem . apiRanges . css ( "display" , "block" ) ;
activeItem . apiPresets . css ( "display" , "block" ) ;
if ( selectedVal === "openai" ) {
activeItem . apiPresets . css ( "display" , "flex" ) ;
}
if ( selectedVal === "textgenerationwebui" || selectedVal === "novel" ) {
console . log ( "enabling amount_gen for ooba/novel" ) ;
activeItem . amountGenElem . find ( 'input' ) . prop ( "disabled" , false ) ;
activeItem . amountGenElem . css ( "opacity" , 1.0 ) ;
}
2023-08-15 03:40:43 +02:00
if ( selectedVal === "novel" ) {
$ ( "#ai_module_block_novel" ) . css ( "display" , "block" ) ;
} else {
$ ( "#ai_module_block_novel" ) . css ( "display" , "none" ) ;
}
2023-07-20 19:32:15 +02:00
// Hide common settings for OpenAI
console . debug ( 'value?' , selectedVal ) ;
if ( selectedVal == "openai" ) {
console . debug ( 'hiding settings?' ) ;
$ ( "#common-gen-settings-block" ) . css ( "display" , "none" ) ;
} else {
$ ( "#common-gen-settings-block" ) . css ( "display" , "block" ) ;
}
main _api = selectedVal ;
online _status = "no_connection" ;
if ( main _api == 'openai' && oai _settings . chat _completion _source == chat _completion _sources . WINDOWAI ) {
$ ( '#api_button_openai' ) . trigger ( 'click' ) ;
}
if ( main _api == "koboldhorde" ) {
is _get _status = true ;
getStatus ( ) ;
getHordeModels ( ) ;
}
2023-06-25 18:20:53 +02:00
switch ( oai _settings . chat _completion _source ) {
2023-07-18 18:33:13 +02:00
case chat _completion _sources . SCALE :
2023-07-24 17:30:31 +02:00
case chat _completion _sources . OPENROUTER :
case chat _completion _sources . WINDOWAI :
case chat _completion _sources . CLAUDE :
case chat _completion _sources . OPENAI :
2023-08-19 17:20:42 +02:00
case chat _completion _sources . AI21 :
2023-07-24 17:30:31 +02:00
default :
setupChatCompletionPromptManager ( oai _settings ) ;
2023-06-25 19:01:09 +02:00
break ;
2023-06-25 18:20:53 +02:00
}
2023-07-20 19:32:15 +02:00
}
////////////////////////////////////////////////////
2023-08-29 00:54:11 +02:00
export async function getUserAvatars ( ) {
2023-07-20 19:32:15 +02:00
const response = await fetch ( "/getuseravatars" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
"" : "" ,
} ) ,
} ) ;
if ( response . ok === true ) {
const getData = await response . json ( ) ;
//background = getData;
//console.log(getData.length);
$ ( "#user_avatar_block" ) . html ( "" ) ; //RossAscends: necessary to avoid doubling avatars each refresh.
$ ( "#user_avatar_block" ) . append ( '<div class="avatar_upload">+</div>' ) ;
for ( var i = 0 ; i < getData . length ; i ++ ) {
//console.log(1);
appendUserAvatar ( getData [ i ] ) ;
}
//var aa = JSON.parse(getData[0]);
//const load_ch_coint = Object.getOwnPropertyNames(getData);
}
}
function highlightSelectedAvatar ( ) {
$ ( "#user_avatar_block" ) . find ( ".avatar" ) . removeClass ( "selected" ) ;
$ ( "#user_avatar_block" )
. find ( ` .avatar[imgfile=' ${ user _avatar } '] ` )
. addClass ( "selected" ) ;
}
function appendUserAvatar ( name ) {
const template = $ ( '#user_avatar_template .avatar-container' ) . clone ( ) ;
const personaName = power _user . personas [ name ] ;
if ( personaName ) {
template . attr ( 'title' , personaName ) ;
} else {
template . attr ( 'title' , '[Unnamed Persona]' ) ;
}
template . find ( '.avatar' ) . attr ( 'imgfile' , name ) ;
template . toggleClass ( 'default_persona' , name === power _user . default _persona )
template . find ( 'img' ) . attr ( 'src' , getUserAvatar ( name ) ) ;
$ ( "#user_avatar_block" ) . append ( template ) ;
highlightSelectedAvatar ( ) ;
}
function reloadUserAvatar ( force = false ) {
$ ( ".mes" ) . each ( function ( ) {
const avatarImg = $ ( this ) . find ( ".avatar img" ) ;
if ( force ) {
avatarImg . attr ( "src" , avatarImg . attr ( "src" ) ) ;
}
if ( $ ( this ) . attr ( "is_user" ) == 'true' && $ ( this ) . attr ( 'force_avatar' ) == 'false' ) {
avatarImg . attr ( "src" , getUserAvatar ( user _avatar ) ) ;
}
} ) ;
}
export function setUserName ( value ) {
name1 = value ;
if ( name1 === undefined || name1 == "" )
name1 = default _user _name ;
console . log ( ` User name changed to ${ name1 } ` ) ;
$ ( "#your_name" ) . val ( name1 ) ;
2023-08-03 13:24:45 +02:00
if ( power _user . persona _show _notifications ) {
toastr . success ( ` Your messages will now be sent as ${ name1 } ` , 'Current persona updated' ) ;
}
2023-07-20 19:32:15 +02:00
saveSettings ( "change_name" ) ;
}
function setUserAvatar ( ) {
user _avatar = $ ( this ) . attr ( "imgfile" ) ;
reloadUserAvatar ( ) ;
saveSettingsDebounced ( ) ;
highlightSelectedAvatar ( ) ;
2023-08-29 00:54:11 +02:00
selectCurrentPersona ( ) ;
2023-09-05 03:09:35 +02:00
$ ( '.zoomed_avatar[forchar]' ) . remove ( ) ;
2023-07-20 19:32:15 +02:00
}
async function uploadUserAvatar ( e ) {
const file = e . target . files [ 0 ] ;
if ( ! file ) {
$ ( "#form_upload_avatar" ) . trigger ( "reset" ) ;
return ;
}
const formData = new FormData ( $ ( "#form_upload_avatar" ) . get ( 0 ) ) ;
const dataUrl = await new Promise ( ( resolve , reject ) => {
const reader = new FileReader ( ) ;
reader . onload = resolve ;
reader . onerror = reject ;
reader . readAsDataURL ( file ) ;
} ) ;
$ ( '#dialogue_popup' ) . addClass ( 'large_dialogue_popup wide_dialogue_popup' ) ;
const confirmation = await callPopup ( getCropPopup ( dataUrl . target . result ) , 'avatarToCrop' ) ;
if ( ! confirmation ) {
return ;
}
let url = "/uploaduseravatar" ;
if ( crop _data !== undefined ) {
url += ` ?crop= ${ encodeURIComponent ( JSON . stringify ( crop _data ) ) } ` ;
}
jQuery . ajax ( {
type : "POST" ,
url : url ,
data : formData ,
beforeSend : ( ) => { } ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( ) {
// If the user uploaded a new avatar, we want to make sure it's not cached
const name = formData . get ( "overwrite_name" ) ;
if ( name ) {
await fetch ( getUserAvatar ( name ) , { cache : "no-cache" } ) ;
reloadUserAvatar ( true ) ;
}
crop _data = undefined ;
await getUserAvatars ( ) ;
} ,
error : ( jqXHR , exception ) => { } ,
} ) ;
// Will allow to select the same file twice in a row
$ ( "#form_upload_avatar" ) . trigger ( "reset" ) ;
}
2023-07-21 15:42:18 +02:00
async function doOnboarding ( avatarId ) {
2023-08-29 17:04:10 +02:00
let simpleUiMode = false ;
2023-07-21 15:42:18 +02:00
const template = $ ( '#onboarding_template .onboarding' ) ;
2023-08-29 17:04:10 +02:00
template . find ( 'input[name="enable_simple_mode"]' ) . on ( 'input' , function ( ) {
simpleUiMode = $ ( this ) . is ( ':checked' ) ;
} ) ;
2023-07-21 15:42:18 +02:00
const userName = await callPopup ( template , 'input' , name1 ) ;
if ( userName ) {
setUserName ( userName ) ;
console . log ( ` Binding persona ${ avatarId } to name ${ userName } ` ) ;
power _user . personas [ avatarId ] = userName ;
power _user . persona _descriptions [ avatarId ] = {
description : '' ,
2023-08-28 23:09:41 +02:00
position : persona _description _positions . IN _PROMPT ,
2023-07-21 15:42:18 +02:00
} ;
}
2023-08-29 17:04:10 +02:00
if ( simpleUiMode ) {
power _user . ui _mode = ui _mode . SIMPLE ;
$ ( '#ui_mode_select' ) . val ( power _user . ui _mode ) ;
switchSimpleMode ( ) ;
}
2023-07-21 15:42:18 +02:00
}
2023-07-20 19:32:15 +02:00
//***************SETTINGS****************//
///////////////////////////////////////////
async function getSettings ( type ) {
const response = await fetch ( "/getsettings" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { } ) ,
cache : "no-cache" ,
} ) ;
if ( ! response . ok ) {
toastr . error ( 'Settings could not be loaded. Try reloading the page.' ) ;
throw new Error ( 'Error getting settings' ) ;
}
const data = await response . json ( ) ;
if ( data . result != "file not find" && data . settings ) {
settings = JSON . parse ( data . settings ) ;
2023-07-21 15:42:18 +02:00
if ( settings . username !== undefined && settings . username !== "" ) {
name1 = settings . username ;
$ ( "#your_name" ) . val ( name1 ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-10 19:56:18 +02:00
// Allow subscribers to mutate settings
2023-06-29 19:34:01 +02:00
eventSource . emit ( event _types . SETTINGS _LOADED _BEFORE , settings ) ;
2023-07-20 19:32:15 +02:00
//Load KoboldAI settings
koboldai _setting _names = data . koboldai _setting _names ;
koboldai _settings = data . koboldai _settings ;
koboldai _settings . forEach ( function ( item , i , arr ) {
koboldai _settings [ i ] = JSON . parse ( item ) ;
} ) ;
let arr _holder = { } ;
$ ( "#settings_perset" ) . empty ( ) ; //RossAscends: uncommented this to prevent settings selector from doubling preset list on refresh
$ ( "#settings_perset" ) . append (
'<option value="gui">GUI KoboldAI Settings</option>'
) ; //adding in the GUI settings, since it is not loaded dynamically
koboldai _setting _names . forEach ( function ( item , i , arr ) {
arr _holder [ item ] = i ;
$ ( "#settings_perset" ) . append ( ` <option value= ${ i } > ${ item } </option> ` ) ;
//console.log('loading preset #'+i+' -- '+item);
} ) ;
koboldai _setting _names = { } ;
koboldai _setting _names = arr _holder ;
preset _settings = settings . preset _settings ;
if ( preset _settings == "gui" ) {
selectKoboldGuiPreset ( ) ;
} else {
if ( typeof koboldai _setting _names [ preset _settings ] !== "undefined" ) {
$ ( ` #settings_perset option[value= ${ koboldai _setting _names [ preset _settings ] } ] ` )
. attr ( "selected" , "true" ) ;
} else {
preset _settings = "gui" ;
selectKoboldGuiPreset ( ) ;
}
}
novelai _setting _names = data . novelai _setting _names ;
novelai _settings = data . novelai _settings ;
novelai _settings . forEach ( function ( item , i , arr ) {
novelai _settings [ i ] = JSON . parse ( item ) ;
} ) ;
arr _holder = { } ;
$ ( "#settings_perset_novel" ) . empty ( ) ;
novelai _setting _names . forEach ( function ( item , i , arr ) {
arr _holder [ item ] = i ;
$ ( "#settings_perset_novel" ) . append ( ` <option value= ${ i } > ${ item } </option> ` ) ;
} ) ;
novelai _setting _names = { } ;
novelai _setting _names = arr _holder ;
//Load AI model config settings
amount _gen = settings . amount _gen ;
if ( settings . max _context !== undefined )
max _context = parseInt ( settings . max _context ) ;
swipes = settings . swipes !== undefined ? ! ! settings . swipes : true ; // enable swipes by default
$ ( '#swipes-checkbox' ) . prop ( 'checked' , swipes ) ; /// swipecode
hideSwipeButtons ( ) ;
showSwipeButtons ( ) ;
// Kobold
2023-07-23 01:09:03 +02:00
loadKoboldSettings ( settings . kai _settings ? ? settings ) ;
2023-07-20 19:32:15 +02:00
// Novel
2023-07-23 01:09:03 +02:00
loadNovelSettings ( settings . nai _settings ? ? settings ) ;
$ ( ` #settings_perset_novel option[value= ${ novelai _setting _names [ nai _settings . preset _settings _novel ] } ] ` ) . attr ( "selected" , "true" ) ;
2023-07-20 19:32:15 +02:00
// TextGen
loadTextGenSettings ( data , settings ) ;
// OpenAI
2023-08-21 23:35:46 +02:00
loadOpenAISettings ( data , settings . oai _settings ? ? settings ) ;
2023-07-20 19:32:15 +02:00
// Horde
loadHordeSettings ( settings ) ;
// Load power user settings
loadPowerUserSettings ( settings , data ) ;
// Load character tags
loadTagsSettings ( settings ) ;
2023-06-29 19:26:20 +02:00
// Allow subscribers to mutate settings
eventSource . emit ( event _types . SETTINGS _LOADED _AFTER , settings ) ;
2023-07-20 19:32:15 +02:00
// Set context size after loading power user (may override the max value)
$ ( "#max_context" ) . val ( max _context ) ;
$ ( "#max_context_counter" ) . text ( ` ${ max _context } ` ) ;
$ ( "#amount_gen" ) . val ( amount _gen ) ;
$ ( "#amount_gen_counter" ) . text ( ` ${ amount _gen } ` ) ;
//Load which API we are using
if ( settings . main _api == undefined ) {
settings . main _api = 'kobold' ;
}
if ( settings . main _api == 'poe' ) {
settings . main _api = 'openai' ;
}
main _api = settings . main _api ;
$ ( '#main_api' ) . val ( main _api ) ;
$ ( "#main_api option[value=" + main _api + "]" ) . attr (
"selected" ,
"true"
) ;
changeMainAPI ( ) ;
//Load User's Name and Avatar
user _avatar = settings . user _avatar ;
2023-07-21 15:42:18 +02:00
firstRun = ! ! settings . firstRun ;
if ( firstRun ) {
await doOnboarding ( user _avatar ) ;
firstRun = false ;
}
2023-07-20 19:32:15 +02:00
reloadUserAvatar ( ) ;
highlightSelectedAvatar ( ) ;
setPersonaDescription ( ) ;
2023-07-30 01:48:08 +02:00
//Load the active character and group
active _character = settings . active _character ;
active _group = settings . active _group ;
2023-07-20 19:32:15 +02:00
//Load the API server URL from settings
api _server = settings . api _server ;
$ ( "#api_url_text" ) . val ( api _server ) ;
2023-08-10 19:45:57 +02:00
setWorldInfoSettings ( settings . world _info _settings ? ? settings , data ) ;
2023-07-20 19:32:15 +02:00
2023-08-03 05:25:24 +02:00
api _server _textgenerationwebui = settings . api _server _textgenerationwebui ;
2023-08-20 06:33:00 +02:00
$ ( "#textgenerationwebui_api_url_text" ) . val ( api _server _textgenerationwebui ) ;
$ ( "#mancer_api_url_text" ) . val ( api _server _textgenerationwebui ) ;
2023-08-03 05:25:24 +02:00
api _use _mancer _webui = settings . api _use _mancer _webui
$ ( '#use-mancer-api-checkbox' ) . prop ( "checked" , api _use _mancer _webui ) ;
$ ( '#use-mancer-api-checkbox' ) . trigger ( "change" ) ;
2023-07-20 19:32:15 +02:00
selected _button = settings . selected _button ;
if ( data . enable _extensions ) {
await loadExtensionSettings ( settings ) ;
eventSource . emit ( event _types . EXTENSION _SETTINGS _LOADED ) ;
}
}
eventSource . emit ( event _types . SETTINGS _LOADED ) ;
}
function selectKoboldGuiPreset ( ) {
$ ( "#settings_perset option[value=gui]" )
. attr ( "selected" , "true" )
. trigger ( "change" ) ;
}
async function saveSettings ( type ) {
2023-08-29 17:04:10 +02:00
if ( ! settingsReady ) {
console . warn ( 'Settings not ready, aborting save' ) ;
return ;
}
2023-07-20 19:32:15 +02:00
//console.log('Entering settings with name1 = '+name1);
return jQuery . ajax ( {
type : "POST" ,
url : "/savesettings" ,
data : JSON . stringify ( {
2023-07-21 15:42:18 +02:00
firstRun : firstRun ,
2023-07-20 19:32:15 +02:00
username : name1 ,
2023-07-30 01:48:08 +02:00
active _character : active _character ,
active _group : active _group ,
2023-07-20 19:32:15 +02:00
api _server : api _server ,
api _server _textgenerationwebui : api _server _textgenerationwebui ,
2023-08-03 05:25:24 +02:00
api _use _mancer _webui : api _use _mancer _webui ,
2023-07-20 19:32:15 +02:00
preset _settings : preset _settings ,
user _avatar : user _avatar ,
amount _gen : amount _gen ,
max _context : max _context ,
main _api : main _api ,
2023-08-10 19:45:57 +02:00
world _info _settings : getWorldInfoSettings ( ) ,
2023-07-20 19:32:15 +02:00
textgenerationwebui _settings : textgenerationwebui _settings ,
swipes : swipes ,
horde _settings : horde _settings ,
power _user : power _user ,
extension _settings : extension _settings ,
tags : tags ,
tag _map : tag _map ,
2023-07-23 01:09:03 +02:00
nai _settings : nai _settings ,
kai _settings : kai _settings ,
2023-08-21 23:35:46 +02:00
oai _settings : oai _settings ,
2023-07-20 19:32:15 +02:00
} , null , 4 ) ,
beforeSend : function ( ) {
if ( type == "change_name" ) {
//let nameBeforeChange = name1;
name1 = $ ( "#your_name" ) . val ( ) ;
//$(`.mes[ch_name="${nameBeforeChange}"]`).attr('ch_name' === name1);
//console.log('beforeSend name1 = ' + nameBeforeChange);
//console.log('new name: ' + name1);
}
} ,
cache : false ,
dataType : "json" ,
contentType : "application/json" ,
//processData: false,
2023-09-08 11:10:41 +02:00
success : async function ( data ) {
2023-07-20 19:32:15 +02:00
//online_status = data.result;
eventSource . emit ( event _types . SETTINGS _UPDATED ) ;
if ( type == "change_name" ) {
clearChat ( ) ;
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-07-20 19:32:15 +02:00
}
} ,
error : function ( jqXHR , exception ) {
toastr . error ( 'Check the server connection and reload the page to prevent data loss.' , 'Settings could not be saved' ) ;
console . log ( exception ) ;
console . log ( jqXHR ) ;
} ,
} ) ;
}
2023-07-23 22:52:31 +02:00
export function setGenerationParamsFromPreset ( preset ) {
if ( preset . genamt !== undefined ) {
amount _gen = preset . genamt ;
$ ( "#amount_gen" ) . val ( amount _gen ) ;
$ ( "#amount_gen_counter" ) . text ( ` ${ amount _gen } ` ) ;
}
if ( preset . max _length !== undefined ) {
2023-09-10 03:08:15 +02:00
const needsUnlock = preset . max _length > MAX _CONTEXT _DEFAULT ;
2023-07-23 22:52:31 +02:00
$ ( '#max_context_unlocked' ) . prop ( 'checked' , needsUnlock ) . trigger ( 'change' ) ;
2023-09-10 03:08:15 +02:00
max _context = preset . max _length ;
2023-07-20 19:32:15 +02:00
2023-07-23 22:52:31 +02:00
$ ( "#max_context" ) . val ( max _context ) ;
$ ( "#max_context_counter" ) . text ( ` ${ max _context } ` ) ;
}
}
2023-07-20 19:32:15 +02:00
// Common code for message editor done and auto-save
function updateMessage ( div ) {
const mesBlock = div . closest ( ".mes_block" ) ;
let text = mesBlock . find ( ".edit_textarea" ) . val ( ) ;
const mes = chat [ this _edit _mes _id ] ;
let regexPlacement ;
if ( mes . is _name && mes . is _user ) {
regexPlacement = regex _placement . USER _INPUT ;
} else if ( mes . is _name && mes . name === name2 ) {
regexPlacement = regex _placement . AI _OUTPUT ;
} else if ( mes . is _name && mes . name !== name2 || mes . extra ? . type === "narrator" ) {
regexPlacement = regex _placement . SLASH _COMMAND ;
}
// Ignore character override if sent as system
text = getRegexedString (
text ,
regexPlacement ,
{ characterOverride : mes . extra ? . type === "narrator" ? undefined : mes . name }
) ;
if ( power _user . trim _spaces ) {
text = text . trim ( ) ;
}
const bias = extractMessageBias ( text ) ;
mes [ "mes" ] = text ;
if ( mes [ "swipe_id" ] !== undefined ) {
mes [ "swipes" ] [ mes [ "swipe_id" ] ] = text ;
}
// editing old messages
if ( ! mes . extra ) {
mes . extra = { } ;
}
if ( mes . is _system || mes . is _user || mes . extra . type === system _message _types . NARRATOR ) {
mes . extra . bias = bias ? ? null ;
} else {
mes . extra . bias = null ;
}
return { mesBlock , text , mes , bias } ;
}
function openMessageDelete ( ) {
closeMessageEditor ( ) ;
hideSwipeButtons ( ) ;
if ( ( this _chid != undefined && ! is _send _press ) || ( selected _group && ! is _group _generating ) ) {
$ ( "#dialogue_del_mes" ) . css ( "display" , "block" ) ;
$ ( "#send_form" ) . css ( "display" , "none" ) ;
$ ( ".del_checkbox" ) . each ( function ( ) {
if ( $ ( this ) . parent ( ) . attr ( "mesid" ) != 0 ) {
$ ( this ) . css ( "display" , "block" ) ;
$ ( this ) . parent ( ) . children ( ".for_checkbox" ) . css ( "display" , "none" ) ;
}
} ) ;
} else {
console . debug ( `
ERR -- could not enter del mode
this _chid : $ { this _chid }
is _send _press : $ { is _send _press }
selected _group : $ { selected _group }
is _group _generating : $ { is _group _generating } ` );
}
is _delete _mode = true ;
}
function messageEditAuto ( div ) {
const { mesBlock , text , mes } = updateMessage ( div ) ;
mesBlock . find ( ".mes_text" ) . val ( '' ) ;
mesBlock . find ( ".mes_text" ) . val ( messageFormatting (
text ,
this _edit _mes _chname ,
mes . is _system ,
mes . is _user ,
) ) ;
saveChatDebounced ( ) ;
}
async function messageEditDone ( div ) {
let { mesBlock , text , mes , bias } = updateMessage ( div ) ;
if ( this _edit _mes _id == 0 ) {
text = substituteParams ( text ) ;
}
mesBlock . find ( ".mes_text" ) . empty ( ) ;
mesBlock . find ( ".mes_edit_buttons" ) . css ( "display" , "none" ) ;
mesBlock . find ( ".mes_buttons" ) . css ( "display" , "" ) ;
mesBlock . find ( ".mes_text" ) . append (
messageFormatting (
text ,
this _edit _mes _chname ,
mes . is _system ,
mes . is _user ,
)
) ;
mesBlock . find ( ".mes_bias" ) . empty ( ) ;
mesBlock . find ( ".mes_bias" ) . append ( messageFormatting ( bias ) ) ;
appendImageToMessage ( mes , div . closest ( ".mes" ) ) ;
addCopyToCodeBlocks ( div . closest ( ".mes" ) ) ;
await eventSource . emit ( event _types . MESSAGE _EDITED , this _edit _mes _id ) ;
this _edit _mes _id = undefined ;
await saveChatConditional ( ) ;
}
2023-08-19 05:48:08 +02:00
/ * *
* Fetches the chat content for each chat file from the server and compiles them into a dictionary .
2023-08-19 14:58:17 +02:00
* The function iterates over a provided list of chat metadata and requests the actual chat content
2023-08-19 05:48:08 +02:00
* for each chat , either as an individual chat or a group chat based on the context .
*
* @ param { Array } data - An array containing metadata about each chat such as file _name .
* @ param { boolean } isGroupChat - A flag indicating if the chat is a group chat .
2023-08-22 12:07:24 +02:00
* @ returns { Promise < Object > } chat _dict - A dictionary where each key is a file _name and the value is the
2023-08-19 05:48:08 +02:00
* corresponding chat content fetched from the server .
* /
export async function getChatsFromFiles ( data , isGroupChat ) {
const context = getContext ( ) ;
let chat _dict = { } ;
let chat _list = Object . values ( data ) . sort ( ( a , b ) => a [ "file_name" ] . localeCompare ( b [ "file_name" ] ) ) . reverse ( ) ;
for ( const { file _name } of chat _list ) {
try {
const endpoint = isGroupChat ? '/getgroupchat' : '/getchat' ;
const requestBody = isGroupChat
? JSON . stringify ( { id : file _name } )
: JSON . stringify ( {
ch _name : characters [ context . characterId ] . name ,
file _name : file _name . replace ( '.jsonl' , '' ) ,
avatar _url : characters [ context . characterId ] . avatar
} ) ;
const chatResponse = await fetch ( endpoint , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : requestBody ,
cache : 'no-cache' ,
} ) ;
if ( ! chatResponse . ok ) {
continue ;
}
const currentChat = await chatResponse . json ( ) ;
if ( ! isGroupChat ) {
// remove the first message, which is metadata, only for individual chats
currentChat . shift ( ) ;
}
chat _dict [ file _name ] = currentChat ;
} catch ( error ) {
console . error ( error ) ;
}
}
return chat _dict ;
}
/ * *
* Fetches the metadata of all past chats related to a specific character based on its avatar URL .
2023-08-19 14:58:17 +02:00
* The function sends a POST request to the server to retrieve all chats for the character . It then
2023-08-19 05:48:08 +02:00
* processes the received data , sorts it by the file name , and returns the sorted data .
*
2023-08-22 12:07:24 +02:00
* @ returns { Promise < Array > } - An array containing metadata of all past chats of the character , sorted
2023-08-19 05:48:08 +02:00
* in descending order by file name . Returns ` undefined ` if the fetch request is unsuccessful .
* /
2023-07-20 19:32:15 +02:00
async function getPastCharacterChats ( ) {
const response = await fetch ( "/getallchatsofcharacter" , {
method : 'POST' ,
body : JSON . stringify ( { avatar _url : characters [ this _chid ] . avatar } ) ,
headers : getRequestHeaders ( ) ,
} ) ;
if ( ! response . ok ) {
return ;
}
let data = await response . json ( ) ;
data = Object . values ( data ) ;
data = data . sort ( ( a , b ) => a [ "file_name" ] . localeCompare ( b [ "file_name" ] ) ) . reverse ( ) ;
return data ;
}
2023-08-19 05:48:08 +02:00
/ * *
* Displays the past chats for a character or a group based on the selected context .
2023-08-19 14:58:17 +02:00
* The function first fetches the chats , processes them , and then displays them in
* the HTML . It also has a built - in search functionality that allows filtering the
2023-08-19 05:48:08 +02:00
* displayed chats based on a search query .
* /
2023-07-20 19:32:15 +02:00
export async function displayPastChats ( ) {
$ ( "#select_chat_div" ) . empty ( ) ;
const group = selected _group ? groups . find ( x => x . id === selected _group ) : null ;
const data = await ( selected _group ? getGroupPastChats ( selected _group ) : getPastCharacterChats ( ) ) ;
const currentChat = selected _group ? group ? . chat _id : characters [ this _chid ] [ "chat" ] ;
const displayName = selected _group ? group ? . name : characters [ this _chid ] . name ;
const avatarImg = selected _group ? group ? . avatar _url : getThumbnailUrl ( 'avatar' , characters [ this _chid ] [ 'avatar' ] ) ;
2023-08-19 05:48:08 +02:00
const rawChats = await getChatsFromFiles ( data , selected _group ) ;
2023-07-20 19:32:15 +02:00
// Sort by last message date descending
data . sort ( ( a , b ) => sortMoments ( timestampToMoment ( a . last _mes ) , timestampToMoment ( b . last _mes ) ) ) ;
2023-08-19 05:48:08 +02:00
console . log ( data ) ;
2023-07-20 19:32:15 +02:00
$ ( "#load_select_chat_div" ) . css ( "display" , "none" ) ;
$ ( "#ChatHistoryCharName" ) . text ( displayName ) ;
2023-08-19 05:48:08 +02:00
const displayChats = ( searchQuery ) => {
$ ( "#select_chat_div" ) . empty ( ) ; // Clear the current chats before appending filtered chats
2023-07-20 19:32:15 +02:00
2023-08-19 05:48:08 +02:00
const filteredData = data . filter ( chat => {
const fileName = chat [ 'file_name' ] ;
const chatContent = rawChats [ fileName ] ;
2023-07-20 19:32:15 +02:00
2023-09-12 17:05:37 +02:00
return chatContent && Object . values ( chatContent ) . some ( message => message ? . mes ? . toLowerCase ( ) ? . includes ( searchQuery . toLowerCase ( ) ) ) ;
2023-08-19 05:48:08 +02:00
} ) ;
console . log ( filteredData ) ;
for ( const key in filteredData ) {
let strlen = 300 ;
let mes = filteredData [ key ] [ "mes" ] ;
if ( mes !== undefined ) {
if ( mes . length > strlen ) {
mes = "..." + mes . substring ( mes . length - strlen ) ;
}
const chat _items = data [ key ] [ "chat_items" ] ;
const file _size = data [ key ] [ "file_size" ] ;
const fileName = data [ key ] [ 'file_name' ] ;
const timestamp = timestampToMoment ( data [ key ] [ 'last_mes' ] ) . format ( 'LL LT' ) ;
const template = $ ( '#past_chat_template .select_chat_block_wrapper' ) . clone ( ) ;
template . find ( '.select_chat_block' ) . attr ( 'file_name' , fileName ) ;
template . find ( '.avatar img' ) . attr ( 'src' , avatarImg ) ;
template . find ( '.select_chat_block_filename' ) . text ( fileName ) ;
template . find ( '.chat_file_size' ) . text ( " (" + file _size + ")" ) ;
template . find ( '.chat_messages_num' ) . text ( " (" + chat _items + " messages)" ) ;
template . find ( '.select_chat_block_mes' ) . text ( mes ) ;
template . find ( '.PastChat_cross' ) . attr ( 'file_name' , fileName ) ;
template . find ( '.chat_messages_date' ) . text ( timestamp ) ;
if ( selected _group ) {
template . find ( '.avatar img' ) . replaceWith ( getGroupAvatar ( group ) ) ;
}
$ ( "#select_chat_div" ) . append ( template ) ;
if ( currentChat === fileName . toString ( ) . replace ( ".jsonl" , "" ) ) {
$ ( "#select_chat_div" ) . find ( ".select_chat_block:last" ) . attr ( "highlight" , true ) ;
}
2023-07-20 19:32:15 +02:00
}
}
}
2023-08-19 05:48:08 +02:00
displayChats ( '' ) ; // Display all by default
const debouncedDisplay = debounce ( ( searchQuery ) => {
displayChats ( searchQuery ) ;
} , 300 ) ;
// Define the search input listener
$ ( "#select_chat_search" ) . on ( "input" , function ( ) {
const searchQuery = $ ( this ) . val ( ) ;
debouncedDisplay ( searchQuery ) ;
} ) ;
2023-07-20 19:32:15 +02:00
}
//************************************************************
//************************Novel.AI****************************
//************************************************************
async function getStatusNovel ( ) {
if ( is _get _status _novel ) {
2023-09-04 17:00:15 +02:00
try {
const result = await loadNovelSubscriptionData ( ) ;
2023-07-20 19:32:15 +02:00
2023-09-04 17:00:15 +02:00
if ( ! result ) {
throw new Error ( 'Could not load subscription data' ) ;
}
2023-07-20 19:32:15 +02:00
2023-09-04 17:00:15 +02:00
online _status = getNovelTier ( ) ;
} catch {
online _status = "no_connection" ;
}
resultCheckStatusNovel ( ) ;
2023-07-20 19:32:15 +02:00
} else {
if ( is _get _status != true && is _get _status _openai != true ) {
online _status = "no_connection" ;
}
}
}
function selectRightMenuWithAnimation ( selectedMenuId ) {
const displayModes = {
'rm_group_chats_block' : 'flex' ,
'rm_api_block' : 'grid' ,
'rm_characters_block' : 'flex' ,
} ;
2023-08-21 16:00:28 +02:00
$ ( '#result_info' ) . toggle ( selectedMenuId === 'rm_ch_create_block' ) ;
2023-07-20 19:32:15 +02:00
document . querySelectorAll ( '#right-nav-panel .right_menu' ) . forEach ( ( menu ) => {
$ ( menu ) . css ( 'display' , 'none' ) ;
if ( selectedMenuId && selectedMenuId . replace ( '#' , '' ) === menu . id ) {
const mode = displayModes [ menu . id ] ? ? 'block' ;
$ ( menu ) . css ( 'display' , mode ) ;
$ ( menu ) . css ( "opacity" , 0.0 ) ;
$ ( menu ) . transition ( {
opacity : 1.0 ,
duration : animation _duration ,
easing : animation _easing ,
complete : function ( ) { } ,
} ) ;
}
} )
}
function select _rm _info ( type , charId , previousCharId = null ) {
if ( ! type ) {
toastr . error ( ` Invalid process (no 'type') ` ) ;
return ;
}
if ( type !== 'group_create' ) {
var displayName = String ( charId ) . replace ( '.png' , '' ) ;
}
if ( type === 'char_delete' ) {
toastr . warning ( ` Character Deleted: ${ displayName } ` ) ;
}
if ( type === 'char_create' ) {
toastr . success ( ` Character Created: ${ displayName } ` ) ;
}
if ( type === 'group_create' ) {
toastr . success ( ` Group Created ` ) ;
}
if ( type === 'group_delete' ) {
toastr . warning ( ` Group Deleted ` ) ;
}
if ( type === 'char_import' ) {
toastr . success ( ` Character Imported: ${ displayName } ` ) ;
}
selectRightMenuWithAnimation ( 'rm_characters_block' ) ;
2023-08-27 03:01:13 +02:00
// Set a timeout so multiple flashes don't overlap
clearTimeout ( importFlashTimeout ) ;
importFlashTimeout = setTimeout ( function ( ) {
2023-07-20 19:32:15 +02:00
if ( type === 'char_import' || type === 'char_create' ) {
2023-08-27 03:01:13 +02:00
// Find the page at which the character is located
const charData = getEntitiesList ( { doFilter : true } ) ;
const charIndex = charData . findIndex ( ( x ) => x ? . item ? . avatar ? . startsWith ( charId ) ) ;
if ( charIndex === - 1 ) {
console . log ( ` Could not find character ${ charId } in the list ` ) ;
return ;
}
2023-07-20 19:32:15 +02:00
try {
2023-08-27 03:01:13 +02:00
const perPage = Number ( localStorage . getItem ( 'Characters_PerPage' ) ) ;
const page = Math . floor ( charIndex / perPage ) + 1 ;
const selector = ` #rm_print_characters_block [title^=" ${ charId } "] ` ;
$ ( '#rm_print_characters_pagination' ) . pagination ( 'go' , page ) ;
waitUntilCondition ( ( ) => document . querySelector ( selector ) !== null ) . then ( ( ) => {
2023-09-08 15:36:00 +02:00
const parent = $ ( '#rm_print_characters_block' ) ;
const element = $ ( selector ) . parent ( ) ;
2023-08-27 03:01:13 +02:00
2023-09-08 15:36:00 +02:00
if ( element . length === 0 ) {
2023-08-27 03:01:13 +02:00
console . log ( ` Could not find element for character ${ charId } ` ) ;
return ;
}
2023-09-08 15:36:00 +02:00
parent . scrollTop ( element . position ( ) . top + parent . scrollTop ( ) ) ;
element . addClass ( 'flash animated' ) ;
2023-07-20 19:32:15 +02:00
setTimeout ( function ( ) {
2023-09-08 15:36:00 +02:00
element . removeClass ( 'flash animated' ) ;
2023-07-20 19:32:15 +02:00
} , 5000 ) ;
2023-08-27 03:01:13 +02:00
} ) ;
2023-07-20 19:32:15 +02:00
} catch ( e ) {
console . error ( e ) ;
}
}
if ( type === 'group_create' ) {
2023-09-08 15:36:00 +02:00
// Find the page at which the character is located
const charData = getEntitiesList ( { doFilter : true } ) ;
const charIndex = charData . findIndex ( ( x ) => String ( x ? . item ? . id ) === String ( charId ) ) ;
if ( charIndex === - 1 ) {
console . log ( ` Could not find group ${ charId } in the list ` ) ;
return ;
}
const perPage = Number ( localStorage . getItem ( 'Characters_PerPage' ) ) ;
const page = Math . floor ( charIndex / perPage ) + 1 ;
$ ( '#rm_print_characters_pagination' ) . pagination ( 'go' , page ) ;
const parent = $ ( '#rm_print_characters_block' ) ;
const selector = ` #rm_print_characters_block [grid=" ${ charId } "] ` ;
2023-07-20 19:32:15 +02:00
try {
2023-09-08 15:36:00 +02:00
waitUntilCondition ( ( ) => document . querySelector ( selector ) !== null ) . then ( ( ) => {
const element = $ ( selector ) ;
parent . scrollTop ( element . position ( ) . top + parent . scrollTop ( ) ) ;
2023-07-20 19:32:15 +02:00
$ ( element ) . addClass ( 'flash animated' ) ;
setTimeout ( function ( ) {
$ ( element ) . removeClass ( 'flash animated' ) ;
} , 5000 ) ;
2023-09-08 15:36:00 +02:00
} ) ;
2023-07-20 19:32:15 +02:00
} catch ( e ) {
console . error ( e ) ;
}
}
2023-08-27 03:01:13 +02:00
} , 250 ) ;
2023-07-20 19:32:15 +02:00
if ( previousCharId ) {
const newId = characters . findIndex ( ( x ) => x . avatar == previousCharId ) ;
if ( newId >= 0 ) {
this _chid = newId ;
}
}
}
export function select _selected _character ( chid ) {
//character select
//console.log('select_selected_character() -- starting with input of -- '+chid+' (name:'+characters[chid].name+')');
select _rm _create ( ) ;
menu _type = "character_edit" ;
$ ( "#delete_button" ) . css ( "display" , "flex" ) ;
$ ( "#export_button" ) . css ( "display" , "flex" ) ;
var display _name = characters [ chid ] . name ;
//create text poles
$ ( "#rm_button_back" ) . css ( "display" , "none" ) ;
//$("#character_import_button").css("display", "none");
$ ( "#create_button" ) . attr ( "value" , "Save" ) ; // what is the use case for this?
$ ( "#dupe_button" ) . show ( ) ;
$ ( "#create_button_label" ) . css ( "display" , "none" ) ;
// Hide the chat scenario button if we're peeking the group member defs
$ ( '#set_chat_scenario' ) . toggle ( ! selected _group ) ;
// Don't update the navbar name if we're peeking the group member defs
if ( ! selected _group ) {
$ ( "#rm_button_selected_ch" ) . children ( "h2" ) . text ( display _name ) ;
}
$ ( "#add_avatar_button" ) . val ( "" ) ;
$ ( "#character_popup_text_h3" ) . text ( characters [ chid ] . name ) ;
$ ( "#character_name_pole" ) . val ( characters [ chid ] . name ) ;
$ ( "#description_textarea" ) . val ( characters [ chid ] . description ) ;
$ ( "#character_world" ) . val ( characters [ chid ] . data ? . extensions ? . world || '' ) ;
$ ( "#creator_notes_textarea" ) . val ( characters [ chid ] . data ? . creator _notes || characters [ chid ] . creatorcomment ) ;
$ ( "#creator_notes_spoiler" ) . text ( characters [ chid ] . data ? . creator _notes || characters [ chid ] . creatorcomment ) ;
$ ( "#character_version_textarea" ) . val ( characters [ chid ] . data ? . character _version || '' ) ;
$ ( "#system_prompt_textarea" ) . val ( characters [ chid ] . data ? . system _prompt || '' ) ;
$ ( "#post_history_instructions_textarea" ) . val ( characters [ chid ] . data ? . post _history _instructions || '' ) ;
$ ( "#tags_textarea" ) . val ( Array . isArray ( characters [ chid ] . data ? . tags ) ? characters [ chid ] . data . tags . join ( ', ' ) : '' ) ;
$ ( "#creator_textarea" ) . val ( characters [ chid ] . data ? . creator ) ;
$ ( "#character_version_textarea" ) . val ( characters [ chid ] . data ? . character _version || '' ) ;
$ ( "#personality_textarea" ) . val ( characters [ chid ] . personality ) ;
$ ( "#firstmessage_textarea" ) . val ( characters [ chid ] . first _mes ) ;
$ ( "#scenario_pole" ) . val ( characters [ chid ] . scenario ) ;
$ ( "#talkativeness_slider" ) . val ( characters [ chid ] . talkativeness || talkativeness _default ) ;
$ ( "#mes_example_textarea" ) . val ( characters [ chid ] . mes _example ) ;
$ ( "#selected_chat_pole" ) . val ( characters [ chid ] . chat ) ;
$ ( "#create_date_pole" ) . val ( characters [ chid ] . create _date ) ;
$ ( "#avatar_url_pole" ) . val ( characters [ chid ] . avatar ) ;
$ ( "#chat_import_avatar_url" ) . val ( characters [ chid ] . avatar ) ;
$ ( "#chat_import_character_name" ) . val ( characters [ chid ] . name ) ;
$ ( "#character_json_data" ) . val ( characters [ chid ] . json _data ) ;
let this _avatar = default _avatar ;
if ( characters [ chid ] . avatar != "none" ) {
this _avatar = getThumbnailUrl ( 'avatar' , characters [ chid ] . avatar ) ;
}
updateFavButtonState ( characters [ chid ] . fav || characters [ chid ] . fav == "true" ) ;
$ ( "#avatar_load_preview" ) . attr ( "src" , this _avatar ) ;
$ ( "#name_div" ) . removeClass ( 'displayBlock' ) ;
$ ( "#name_div" ) . addClass ( 'displayNone' ) ;
$ ( "#renameCharButton" ) . css ( "display" , "" ) ;
$ ( '.open_alternate_greetings' ) . data ( 'chid' , chid ) ;
$ ( '#set_character_world' ) . data ( 'chid' , chid ) ;
setWorldInfoButtonClass ( chid ) ;
checkEmbeddedWorld ( chid ) ;
$ ( "#form_create" ) . attr ( "actiontype" , "editcharacter" ) ;
saveSettingsDebounced ( ) ;
}
function select _rm _create ( ) {
menu _type = "create" ;
//console.log('select_rm_Create() -- selected button: '+selected_button);
if ( selected _button == "create" ) {
if ( create _save . avatar != "" ) {
$ ( "#add_avatar_button" ) . get ( 0 ) . files = create _save . avatar ;
read _avatar _load ( $ ( "#add_avatar_button" ) . get ( 0 ) ) ;
}
}
selectRightMenuWithAnimation ( 'rm_ch_create_block' ) ;
$ ( '#set_chat_scenario' ) . hide ( ) ;
$ ( "#delete_button_div" ) . css ( "display" , "none" ) ;
$ ( "#delete_button" ) . css ( "display" , "none" ) ;
$ ( "#export_button" ) . css ( "display" , "none" ) ;
$ ( "#create_button_label" ) . css ( "display" , "" ) ;
$ ( "#create_button" ) . attr ( "value" , "Create" ) ;
$ ( "#dupe_button" ) . hide ( ) ;
//create text poles
$ ( "#rm_button_back" ) . css ( "display" , "" ) ;
$ ( "#character_import_button" ) . css ( "display" , "" ) ;
$ ( "#character_popup_text_h3" ) . text ( "Create character" ) ;
$ ( "#character_name_pole" ) . val ( create _save . name ) ;
$ ( "#description_textarea" ) . val ( create _save . description ) ;
$ ( '#character_world' ) . val ( create _save . world ) ;
$ ( "#creator_notes_textarea" ) . val ( create _save . creator _notes ) ;
$ ( "#creator_notes_spoiler" ) . text ( create _save . creator _notes ) ;
$ ( "#post_history_instructions_textarea" ) . val ( create _save . post _history _instructions ) ;
$ ( "#system_prompt_textarea" ) . val ( create _save . system _prompt ) ;
$ ( "#tags_textarea" ) . val ( create _save . tags ) ;
$ ( "#creator_textarea" ) . val ( create _save . creator ) ;
$ ( "#character_version_textarea" ) . val ( create _save . character _version ) ;
$ ( "#personality_textarea" ) . val ( create _save . personality ) ;
$ ( "#firstmessage_textarea" ) . val ( create _save . first _message ) ;
$ ( "#talkativeness_slider" ) . val ( create _save . talkativeness ) ;
$ ( "#scenario_pole" ) . val ( create _save . scenario ) ;
$ ( "#mes_example_textarea" ) . val ( create _save . mes _example . trim ( ) . length === 0 ? '<START>' : create _save . mes _example ) ;
$ ( '#character_json_data' ) . val ( '' ) ;
$ ( "#avatar_div" ) . css ( "display" , "flex" ) ;
$ ( "#avatar_load_preview" ) . attr ( "src" , default _avatar ) ;
$ ( "#renameCharButton" ) . css ( 'display' , 'none' ) ;
$ ( "#name_div" ) . removeClass ( 'displayNone' ) ;
$ ( "#name_div" ) . addClass ( 'displayBlock' ) ;
$ ( '.open_alternate_greetings' ) . data ( 'chid' , undefined ) ;
$ ( '#set_character_world' ) . data ( 'chid' , undefined ) ;
setWorldInfoButtonClass ( undefined , ! ! create _save . world ) ;
updateFavButtonState ( false ) ;
checkEmbeddedWorld ( ) ;
$ ( "#form_create" ) . attr ( "actiontype" , "createcharacter" ) ;
}
function select _rm _characters ( ) {
2023-08-27 01:13:46 +02:00
const doFullRefresh = menu _type === 'characters' ;
2023-07-20 19:32:15 +02:00
menu _type = "characters" ;
selectRightMenuWithAnimation ( 'rm_characters_block' ) ;
2023-08-27 01:13:46 +02:00
printCharacters ( doFullRefresh ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-25 16:05:51 +02:00
/ * *
* Sets a prompt injection to insert custom text into any outgoing prompt . For use in UI extensions .
* @ param { string } key Prompt injection id .
* @ param { string } value Prompt injection value .
* @ param { number } position Insertion position . 0 is after story string , 1 is in - chat with custom depth .
* @ param { number } depth Insertion depth . 0 represets the last message in context . Expected values up to 100.
* /
2023-09-08 00:26:26 +02:00
export function setExtensionPrompt ( key , value , position , depth ) {
2023-08-25 16:05:51 +02:00
extension _prompts [ key ] = { value : String ( value ) , position : Number ( position ) , depth : Number ( depth ) } ;
2023-07-20 19:32:15 +02:00
}
2023-08-25 16:05:51 +02:00
/ * *
* Adds or updates the metadata for the currently active chat .
* @ param { Object } newValues An object with collection of new values to be added into the metadata .
* @ param { boolean } reset Should a metadata be reset by this call .
* /
2023-07-20 19:32:15 +02:00
function updateChatMetadata ( newValues , reset ) {
chat _metadata = reset ? { ... newValues } : { ... chat _metadata , ... newValues } ;
}
function updateFavButtonState ( state ) {
fav _ch _checked = state ;
$ ( "#fav_checkbox" ) . val ( fav _ch _checked ) ;
$ ( "#favorite_button" ) . toggleClass ( 'fav_on' , fav _ch _checked ) ;
$ ( "#favorite_button" ) . toggleClass ( 'fav_off' , ! fav _ch _checked ) ;
}
export function setScenarioOverride ( ) {
if ( ! selected _group && ! this _chid ) {
console . warn ( 'setScenarioOverride() -- no selected group or character' ) ;
return ;
}
const template = $ ( '#scenario_override_template .scenario_override' ) . clone ( ) ;
const metadataValue = chat _metadata [ 'scenario' ] || '' ;
const isGroup = ! ! selected _group ;
template . find ( '[data-group="true"]' ) . toggle ( isGroup ) ;
template . find ( '[data-character="true"]' ) . toggle ( ! isGroup ) ;
template . find ( '.chat_scenario' ) . text ( metadataValue ) . on ( 'input' , onScenarioOverrideInput ) ;
template . find ( '.remove_scenario_override' ) . on ( 'click' , onScenarioOverrideRemoveClick ) ;
callPopup ( template , 'text' ) ;
}
function onScenarioOverrideInput ( ) {
const value = $ ( this ) . val ( ) ;
const metadata = { scenario : value , } ;
updateChatMetadata ( metadata , false ) ;
saveMetadataDebounced ( ) ;
}
function onScenarioOverrideRemoveClick ( ) {
$ ( this ) . closest ( '.scenario_override' ) . find ( '.chat_scenario' ) . val ( '' ) . trigger ( 'input' ) ;
}
2023-07-22 21:14:16 +02:00
function callPopup ( text , type , inputValue = '' , { okButton , rows , wide , large } = { } ) {
2023-07-20 19:32:15 +02:00
if ( type ) {
popup _type = type ;
}
2023-07-22 21:14:16 +02:00
if ( wide ) {
$ ( "#dialogue_popup" ) . addClass ( "wide_dialogue_popup" ) ;
}
if ( large ) {
$ ( "#dialogue_popup" ) . addClass ( "large_dialogue_popup" ) ;
}
2023-07-20 19:32:15 +02:00
$ ( "#dialogue_popup_cancel" ) . css ( "display" , "inline-block" ) ;
switch ( popup _type ) {
case "avatarToCrop" :
$ ( "#dialogue_popup_ok" ) . text ( okButton ? ? "Accept" ) ;
break ;
case "text" :
case "alternate_greeting" :
case "char_not_selected" :
$ ( "#dialogue_popup_ok" ) . text ( okButton ? ? "Ok" ) ;
$ ( "#dialogue_popup_cancel" ) . css ( "display" , "none" ) ;
break ;
case "delete_extension" :
$ ( "#dialogue_popup_ok" ) . text ( okButton ? ? "Ok" ) ;
break ;
case "new_chat" :
case "confirm" :
$ ( "#dialogue_popup_ok" ) . text ( okButton ? ? "Yes" ) ;
break ;
case "del_group" :
case "rename_chat" :
case "del_chat" :
default :
$ ( "#dialogue_popup_ok" ) . text ( okButton ? ? "Delete" ) ;
}
$ ( "#dialogue_popup_input" ) . val ( inputValue ) ;
$ ( "#dialogue_popup_input" ) . attr ( "rows" , rows ? ? 1 ) ;
if ( popup _type == 'input' ) {
$ ( "#dialogue_popup_input" ) . css ( "display" , "block" ) ;
$ ( "#dialogue_popup_ok" ) . text ( okButton ? ? "Save" ) ;
}
else {
$ ( "#dialogue_popup_input" ) . css ( "display" , "none" ) ;
}
$ ( "#dialogue_popup_text" ) . empty ( ) . append ( text ) ;
$ ( "#shadow_popup" ) . css ( "display" , "block" ) ;
if ( popup _type == 'input' ) {
$ ( "#dialogue_popup_input" ) . focus ( ) ;
}
if ( popup _type == 'avatarToCrop' ) {
// unset existing data
crop _data = undefined ;
$ ( '#avatarToCrop' ) . cropper ( {
aspectRatio : 2 / 3 ,
autoCropArea : 1 ,
viewMode : 2 ,
rotatable : false ,
crop : function ( event ) {
crop _data = event . detail ;
crop _data . want _resize = ! power _user . never _resize _avatars
}
} ) ;
}
$ ( "#shadow_popup" ) . transition ( {
opacity : 1 ,
duration : 200 ,
easing : animation _easing ,
} ) ;
return new Promise ( ( resolve ) => {
dialogueResolve = resolve ;
} ) ;
}
function read _bg _load ( input ) {
if ( input . files && input . files [ 0 ] ) {
var reader = new FileReader ( ) ;
reader . onload = function ( e ) {
$ ( "#bg_load_preview" )
. attr ( "src" , e . target . result )
. width ( 103 )
. height ( 83 ) ;
var formData = new FormData ( $ ( "#form_bg_download" ) . get ( 0 ) ) ;
//console.log(formData);
jQuery . ajax ( {
type : "POST" ,
url : "/downloadbackground" ,
data : formData ,
beforeSend : function ( ) {
} ,
cache : false ,
contentType : false ,
processData : false ,
success : function ( html ) {
setBackground ( html ) ;
$ ( "#bg1" ) . css (
"background-image" ,
` url(" ${ e . target . result } ") `
) ;
$ ( "#form_bg_download" ) . after ( getBackgroundFromTemplate ( html ) ) ;
} ,
error : function ( jqXHR , exception ) {
console . log ( exception ) ;
console . log ( jqXHR ) ;
} ,
} ) ;
} ;
reader . readAsDataURL ( input . files [ 0 ] ) ;
}
}
function showSwipeButtons ( ) {
if ( chat . length === 0 ) {
return ;
}
if (
chat [ chat . length - 1 ] . is _system ||
! swipes ||
$ ( '.mes:last' ) . attr ( 'mesid' ) < 0 ||
chat [ chat . length - 1 ] . is _user ||
chat [ chat . length - 1 ] . extra ? . image ||
count _view _mes < 1 ||
( selected _group && is _group _generating )
) { return ; }
// swipe_id should be set if alternate greetings are added
if ( chat . length == 1 && chat [ 0 ] . swipe _id === undefined ) {
return ;
}
//had to add this to make the swipe counter work
//(copied from the onclick functions for swipe buttons..
//don't know why the array isn't set for non-swipe messsages in Generate or addOneMessage..)
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] === undefined ) { // if there is no swipe-message in the last spot of the chat array
chat [ chat . length - 1 ] [ 'swipe_id' ] = 0 ; // set it to id 0
chat [ chat . length - 1 ] [ 'swipes' ] = [ ] ; // empty the array
chat [ chat . length - 1 ] [ 'swipes' ] [ 0 ] = chat [ chat . length - 1 ] [ 'mes' ] ; //assign swipe array with last message from chat
}
const currentMessage = $ ( "#chat" ) . children ( ) . filter ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) ;
const swipeId = chat [ chat . length - 1 ] . swipe _id ;
var swipesCounterHTML = ( ` ${ ( swipeId + 1 ) } / ${ ( chat [ chat . length - 1 ] . swipes . length ) } ` ) ;
2023-09-07 15:45:19 +02:00
if ( swipeId !== undefined && chat [ chat . length - 1 ] . swipes . length > 1 ) {
2023-07-20 19:32:15 +02:00
currentMessage . children ( '.swipe_left' ) . css ( 'display' , 'flex' ) ;
}
//only show right when generate is off, or when next right swipe would not make a generate happen
if ( is _send _press === false || chat [ chat . length - 1 ] . swipes . length >= swipeId ) {
currentMessage . children ( '.swipe_right' ) . css ( 'display' , 'flex' ) ;
currentMessage . children ( '.swipe_right' ) . css ( 'opacity' , '0.3' ) ;
}
//console.log((chat[chat.length - 1]));
if ( ( chat [ chat . length - 1 ] . swipes . length - swipeId ) === 1 ) {
//console.log('highlighting R swipe');
currentMessage . children ( '.swipe_right' ) . css ( 'opacity' , '0.7' ) ;
}
//console.log(swipesCounterHTML);
$ ( ".swipes-counter" ) . html ( swipesCounterHTML ) ;
//console.log(swipeId);
//console.log(chat[chat.length - 1].swipes.length);
}
function hideSwipeButtons ( ) {
//console.log('hideswipebuttons entered');
$ ( "#chat" ) . children ( ) . filter ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . children ( '.swipe_right' ) . css ( 'display' , 'none' ) ;
$ ( "#chat" ) . children ( ) . filter ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . children ( '.swipe_left' ) . css ( 'display' , 'none' ) ;
}
2023-08-29 00:54:11 +02:00
export async function saveMetadata ( ) {
2023-07-20 19:32:15 +02:00
if ( selected _group ) {
2023-08-23 20:32:38 +02:00
await editGroup ( selected _group , true , false ) ;
2023-07-20 19:32:15 +02:00
}
else {
2023-08-23 20:32:38 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
}
}
export async function saveChatConditional ( ) {
2023-08-23 20:32:38 +02:00
try {
await waitUntilCondition ( ( ) => ! isChatSaving , durationSaveEdit , 100 ) ;
} catch {
console . warn ( 'Timeout waiting for chat to save' ) ;
return ;
2023-07-20 19:32:15 +02:00
}
2023-08-23 01:38:43 +02:00
2023-08-23 20:32:38 +02:00
try {
isChatSaving = true ;
if ( selected _group ) {
await saveGroupChat ( selected _group , true ) ;
}
else {
await saveChat ( ) ;
}
// Save token cache to IndexedDB storage
saveTokenCache ( ) ;
} catch ( error ) {
console . error ( 'Error saving chat' , error ) ;
} finally {
isChatSaving = false ;
}
2023-07-20 19:32:15 +02:00
}
async function importCharacterChat ( formData ) {
await jQuery . ajax ( {
type : "POST" ,
url : "/importchat" ,
data : formData ,
beforeSend : function ( ) {
} ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( data ) {
if ( data . res ) {
await displayPastChats ( ) ;
}
} ,
error : function ( ) {
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
} ,
} ) ;
}
function updateViewMessageIds ( ) {
2023-09-11 00:07:45 +02:00
const minId = getFirstDisplayedMessageId ( ) ;
2023-07-20 19:32:15 +02:00
$ ( '#chat' ) . find ( ".mes" ) . each ( function ( index , element ) {
2023-09-11 00:07:45 +02:00
$ ( element ) . attr ( "mesid" , minId + index ) ;
$ ( element ) . find ( '.mesIDDisplay' ) . text ( ` # ${ minId + index } ` ) ;
2023-07-20 19:32:15 +02:00
} ) ;
$ ( '#chat .mes' ) . removeClass ( 'last_mes' ) ;
$ ( '#chat .mes' ) . last ( ) . addClass ( 'last_mes' ) ;
updateEditArrowClasses ( ) ;
}
2023-09-11 00:07:45 +02:00
function getFirstDisplayedMessageId ( ) {
const allIds = Array . from ( document . querySelectorAll ( '#chat .mes' ) ) . map ( el => Number ( el . getAttribute ( 'mesid' ) ) ) . filter ( x => ! isNaN ( x ) ) ;
const minId = Math . min ( ... allIds ) ;
return minId ;
}
2023-07-20 19:32:15 +02:00
function updateEditArrowClasses ( ) {
$ ( "#chat .mes .mes_edit_up" ) . removeClass ( "disabled" ) ;
$ ( "#chat .mes .mes_edit_down" ) . removeClass ( "disabled" ) ;
if ( this _edit _mes _id !== undefined ) {
const down = $ ( ` #chat .mes[mesid=" ${ this _edit _mes _id } "] .mes_edit_down ` ) ;
const up = $ ( ` #chat .mes[mesid=" ${ this _edit _mes _id } "] .mes_edit_up ` ) ;
const lastId = Number ( $ ( "#chat .mes" ) . last ( ) . attr ( "mesid" ) ) ;
const firstId = Number ( $ ( "#chat .mes" ) . first ( ) . attr ( "mesid" ) ) ;
if ( lastId == Number ( this _edit _mes _id ) ) {
down . addClass ( "disabled" ) ;
}
if ( firstId == Number ( this _edit _mes _id ) ) {
up . addClass ( "disabled" ) ;
}
}
}
function closeMessageEditor ( ) {
if ( this _edit _mes _id ) {
$ ( ` #chat .mes[mesid=" ${ this _edit _mes _id } "] .mes_edit_cancel ` ) . click ( ) ;
}
}
function setGenerationProgress ( progress ) {
if ( ! progress ) {
$ ( '#send_textarea' ) . css ( { 'background' : '' , 'transition' : '' } ) ;
}
else {
$ ( '#send_textarea' ) . css ( {
'background' : ` linear-gradient(90deg, #008000d6 ${ progress } %, transparent ${ progress } %) ` ,
'transition' : '0.25s ease-in-out'
} ) ;
}
}
function isHordeGenerationNotAllowed ( ) {
if ( main _api == "koboldhorde" && preset _settings == "gui" ) {
toastr . error ( 'GUI Settings preset is not supported for Horde. Please select another preset.' ) ;
return true ;
}
return false ;
}
export function cancelTtsPlay ( ) {
if ( 'speechSynthesis' in window ) {
speechSynthesis . cancel ( ) ;
}
}
async function deleteMessageImage ( ) {
const value = await callPopup ( "<h3>Delete image from message?<br>This action can't be undone.</h3>" , 'confirm' ) ;
if ( ! value ) {
return ;
}
const mesBlock = $ ( this ) . closest ( '.mes' ) ;
const mesId = mesBlock . attr ( 'mesid' ) ;
const message = chat [ mesId ] ;
delete message . extra . image ;
delete message . extra . inline _image ;
mesBlock . find ( '.mes_img_container' ) . removeClass ( 'img_extra' ) ;
mesBlock . find ( '.mes_img' ) . attr ( 'src' , '' ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
}
function enlargeMessageImage ( ) {
const mesBlock = $ ( this ) . closest ( '.mes' ) ;
const mesId = mesBlock . attr ( 'mesid' ) ;
const message = chat [ mesId ] ;
const imgSrc = message ? . extra ? . image ;
if ( ! imgSrc ) {
return ;
}
const img = document . createElement ( 'img' ) ;
img . classList . add ( 'img_enlarged' ) ;
img . src = imgSrc ;
2023-09-13 00:40:01 +02:00
callPopup ( img . outerHTML , 'text' , '' , { wide : true , large : true } ) ;
2023-07-20 19:32:15 +02:00
}
function updateAlternateGreetingsHintVisibility ( root ) {
const numberOfGreetings = root . find ( '.alternate_greetings_list .alternate_greeting' ) . length ;
$ ( root ) . find ( '.alternate_grettings_hint' ) . toggle ( numberOfGreetings == 0 ) ;
}
function openCharacterWorldPopup ( ) {
const chid = $ ( '#set_character_world' ) . data ( 'chid' ) ;
if ( menu _type != 'create' && chid == undefined ) {
toastr . error ( 'Does not have an Id for this character in world select menu.' ) ;
return ;
}
async function onSelectCharacterWorld ( ) {
const value = $ ( '.character_world_info_selector' ) . find ( 'option:selected' ) . val ( ) ;
const worldIndex = value !== '' ? Number ( value ) : NaN ;
const name = ! isNaN ( worldIndex ) ? world _names [ worldIndex ] : '' ;
const previousValue = $ ( '#character_world' ) . val ( ) ;
$ ( '#character_world' ) . val ( name ) ;
console . debug ( 'Character world selected:' , name ) ;
if ( menu _type == 'create' ) {
create _save . world = name ;
} else {
if ( previousValue && ! name ) {
try {
// Dirty hack to remove embedded lorebook from character JSON data.
const data = JSON . parse ( $ ( '#character_json_data' ) . val ( ) ) ;
if ( data ? . data ? . character _book ) {
data . data . character _book = undefined ;
}
$ ( '#character_json_data' ) . val ( JSON . stringify ( data ) ) ;
toastr . info ( 'Embedded lorebook will be removed from this character.' ) ;
} catch {
console . error ( 'Failed to parse character JSON data.' ) ;
}
}
await createOrEditCharacter ( ) ;
}
setWorldInfoButtonClass ( undefined , ! ! value ) ;
}
function onExtraWorldInfoChanged ( ) {
const selectedWorlds = $ ( '.character_extra_world_info_selector' ) . val ( ) ;
let charLore = world _info . charLore ? ? [ ] ;
// TODO: Maybe make this utility function not use the window context?
const fileName = getCharaFilename ( chid ) ;
const tempExtraBooks = selectedWorlds . map ( ( index ) => world _names [ index ] ) . filter ( ( e ) => e !== undefined ) ;
const existingCharIndex = charLore . findIndex ( ( e ) => e . name === fileName ) ;
if ( existingCharIndex === - 1 ) {
const newCharLoreEntry = {
name : fileName ,
extraBooks : tempExtraBooks
}
charLore . push ( newCharLoreEntry ) ;
} else if ( tempExtraBooks . length === 0 ) {
charLore . splice ( existingCharIndex , 1 ) ;
} else {
charLore [ existingCharIndex ] . extraBooks = tempExtraBooks ;
}
Object . assign ( world _info , { charLore : charLore } ) ;
saveSettingsDebounced ( ) ;
}
const template = $ ( '#character_world_template .character_world' ) . clone ( ) ;
const select = template . find ( '.character_world_info_selector' ) ;
const extraSelect = template . find ( '.character_extra_world_info_selector' ) ;
const name = ( menu _type == 'create' ? create _save . name : characters [ chid ] ? . data ? . name ) || 'Nameless' ;
const worldId = ( menu _type == 'create' ? create _save . world : characters [ chid ] ? . data ? . extensions ? . world ) || '' ;
template . find ( '.character_name' ) . text ( name ) ;
// Not needed on mobile
2023-08-24 22:52:03 +02:00
const deviceInfo = getDeviceInfo ( ) ;
2023-07-20 19:32:15 +02:00
if ( deviceInfo && deviceInfo . device . type === 'desktop' ) {
$ ( extraSelect ) . select2 ( {
width : '100%' ,
placeholder : 'No auxillary Lorebooks set. Click here to select.' ,
allowClear : true ,
closeOnSelect : false ,
} ) ;
}
// Apped to base dropdown
world _names . forEach ( ( item , i ) => {
const option = document . createElement ( 'option' ) ;
option . value = i ;
option . innerText = item ;
option . selected = item === worldId ;
select . append ( option ) ;
} ) ;
// Append to extras dropdown
if ( world _names . length > 0 ) {
extraSelect . empty ( ) ;
}
world _names . forEach ( ( item , i ) => {
const option = document . createElement ( 'option' ) ;
option . value = i ;
option . innerText = item ;
const existingCharLore = world _info . charLore ? . find ( ( e ) => e . name === getCharaFilename ( ) ) ;
if ( existingCharLore ) {
option . selected = existingCharLore . extraBooks . includes ( item ) ;
} else {
option . selected = false ;
}
extraSelect . append ( option ) ;
} ) ;
select . on ( 'change' , onSelectCharacterWorld ) ;
extraSelect . on ( 'mousedown change' , async function ( e ) {
// If there's no world names, don't do anything
if ( world _names . length === 0 ) {
e . preventDefault ( ) ;
return ;
}
onExtraWorldInfoChanged ( ) ;
} ) ;
callPopup ( template , 'text' ) ;
}
function openAlternateGreetings ( ) {
const chid = $ ( '.open_alternate_greetings' ) . data ( 'chid' ) ;
if ( menu _type != 'create' && chid === undefined ) {
toastr . error ( 'Does not have an Id for this character in editor menu.' ) ;
return ;
} else {
// If the character does not have alternate greetings, create an empty array
if ( chid && Array . isArray ( characters [ chid ] . data . alternate _greetings ) == false ) {
characters [ chid ] . data . alternate _greetings = [ ] ;
}
}
const template = $ ( '#alternate_greetings_template .alternate_grettings' ) . clone ( ) ;
const getArray = ( ) => menu _type == 'create' ? create _save . alternate _greetings : characters [ chid ] . data . alternate _greetings ;
for ( let index = 0 ; index < getArray ( ) . length ; index ++ ) {
addAlternateGreeting ( template , getArray ( ) [ index ] , index , getArray ) ;
}
template . find ( '.add_alternate_greeting' ) . on ( 'click' , function ( ) {
const array = getArray ( ) ;
const index = array . length ;
array . push ( default _ch _mes ) ;
addAlternateGreeting ( template , default _ch _mes , index , getArray ) ;
updateAlternateGreetingsHintVisibility ( template ) ;
} ) ;
updateAlternateGreetingsHintVisibility ( template ) ;
callPopup ( template , 'alternate_greeting' ) ;
}
function addAlternateGreeting ( template , greeting , index , getArray ) {
const greetingBlock = $ ( '#alternate_greeting_form_template .alternate_greeting' ) . clone ( ) ;
greetingBlock . find ( '.alternate_greeting_text' ) . on ( 'input' , async function ( ) {
const value = $ ( this ) . val ( ) ;
const array = getArray ( ) ;
array [ index ] = value ;
} ) . val ( greeting ) ;
greetingBlock . find ( '.greeting_index' ) . text ( index + 1 ) ;
greetingBlock . find ( '.delete_alternate_greeting' ) . on ( 'click' , async function ( ) {
if ( confirm ( 'Are you sure you want to delete this alternate greeting?' ) ) {
const array = getArray ( ) ;
array . splice ( index , 1 ) ;
// We need to reopen the popup to update the index numbers
openAlternateGreetings ( ) ;
}
} ) ;
template . find ( '.alternate_greetings_list' ) . append ( greetingBlock ) ;
}
async function createOrEditCharacter ( e ) {
$ ( "#rm_info_avatar" ) . html ( "" ) ;
let save _name = create _save . name ;
var formData = new FormData ( $ ( "#form_create" ) . get ( 0 ) ) ;
formData . set ( 'fav' , fav _ch _checked ) ;
if ( $ ( "#form_create" ) . attr ( "actiontype" ) == "createcharacter" ) {
if ( $ ( "#character_name_pole" ) . val ( ) . length > 0 ) {
//if the character name text area isn't empty (only posible when creating a new character)
let url = "/createcharacter" ;
if ( crop _data != undefined ) {
url += ` ?crop= ${ encodeURIComponent ( JSON . stringify ( crop _data ) ) } ` ;
}
formData . delete ( 'alternate_greetings' ) ;
for ( const value of create _save . alternate _greetings ) {
formData . append ( 'alternate_greetings' , value ) ;
}
await jQuery . ajax ( {
type : "POST" ,
url : url ,
data : formData ,
beforeSend : function ( ) {
$ ( "#create_button" ) . attr ( "disabled" , true ) ;
$ ( "#create_button" ) . attr ( "value" , "⏳" ) ;
} ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( html ) {
$ ( "#character_cross" ) . trigger ( 'click' ) ; //closes the advanced character editing popup
const fields = [
{ id : '#character_name_pole' , callback : value => create _save . name = value } ,
{ id : '#description_textarea' , callback : value => create _save . description = value } ,
{ id : '#creator_notes_textarea' , callback : value => create _save . creator _notes = value } ,
{ id : '#character_version_textarea' , callback : value => create _save . character _version = value } ,
{ id : '#post_history_instructions_textarea' , callback : value => create _save . post _history _instructions = value } ,
{ id : '#system_prompt_textarea' , callback : value => create _save . system _prompt = value } ,
{ id : '#tags_textarea' , callback : value => create _save . tags = value } ,
{ id : '#creator_textarea' , callback : value => create _save . creator = value } ,
{ id : '#personality_textarea' , callback : value => create _save . personality = value } ,
{ id : '#firstmessage_textarea' , callback : value => create _save . first _message = value } ,
{ id : '#talkativeness_slider' , callback : value => create _save . talkativeness = value , defaultValue : talkativeness _default } ,
{ id : '#scenario_pole' , callback : value => create _save . scenario = value } ,
{ id : '#mes_example_textarea' , callback : value => create _save . mes _example = value } ,
{ id : '#character_json_data' , callback : ( ) => { } } ,
{ id : '#alternate_greetings_template' , callback : value => create _save . alternate _greetings = value , defaultValue : [ ] } ,
{ id : '#character_world' , callback : value => create _save . world = value } ,
] ;
fields . forEach ( field => {
const fieldValue = field . defaultValue !== undefined ? field . defaultValue : '' ;
$ ( field . id ) . val ( fieldValue ) ;
field . callback && field . callback ( fieldValue ) ;
} ) ;
$ ( "#character_popup_text_h3" ) . text ( "Create character" ) ;
create _save . avatar = "" ;
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
$ ( "#add_avatar_button" ) . replaceWith (
$ ( "#add_avatar_button" ) . val ( "" ) . clone ( true )
) ;
$ ( "#create_button" ) . attr ( "value" , "✅" ) ;
let oldSelectedChar = null ;
if ( this _chid != undefined && this _chid != "invalid-safety-id" ) {
oldSelectedChar = characters [ this _chid ] . avatar ;
}
console . log ( ` new avatar id: ${ html } ` ) ;
createTagMapFromList ( "#tagList" , html ) ;
await getCharacters ( ) ;
select _rm _info ( ` char_create ` , html , oldSelectedChar ) ;
crop _data = undefined ;
} ,
error : function ( jqXHR , exception ) {
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
} ,
} ) ;
} else {
2023-08-21 14:32:27 +02:00
toastr . error ( 'Name is required' ) ;
2023-07-20 19:32:15 +02:00
}
} else {
let url = '/editcharacter' ;
if ( crop _data != undefined ) {
url += ` ?crop= ${ encodeURIComponent ( JSON . stringify ( crop _data ) ) } ` ;
}
formData . delete ( 'alternate_greetings' ) ;
const chid = $ ( '.open_alternate_greetings' ) . data ( 'chid' ) ;
if ( chid && Array . isArray ( characters [ chid ] ? . data ? . alternate _greetings ) ) {
for ( const value of characters [ chid ] . data . alternate _greetings ) {
formData . append ( 'alternate_greetings' , value ) ;
}
}
await jQuery . ajax ( {
type : "POST" ,
url : url ,
data : formData ,
beforeSend : function ( ) {
$ ( "#create_button" ) . attr ( "disabled" , true ) ;
$ ( "#create_button" ) . attr ( "value" , "Save" ) ;
} ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( html ) {
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
2023-08-19 14:58:17 +02:00
await getOneCharacter ( formData . get ( 'avatar_url' ) ) ;
2023-09-13 12:12:19 +02:00
favsToHotswap ( ) ; // Update fav state
2023-07-20 19:32:15 +02:00
$ ( "#add_avatar_button" ) . replaceWith (
$ ( "#add_avatar_button" ) . val ( "" ) . clone ( true )
) ;
$ ( "#create_button" ) . attr ( "value" , "Save" ) ;
crop _data = undefined ;
2023-08-18 15:45:40 +02:00
eventSource . emit ( event _types . CHARACTER _EDITED , { detail : { id : this _chid , character : characters [ this _chid ] } } ) ;
2023-08-23 20:32:38 +02:00
if ( chat . length === 1 && ! selected _group ) {
const firstMessage = getFirstMessage ( ) ;
chat [ 0 ] = firstMessage ;
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
clearChat ( ) ;
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-08-23 20:32:38 +02:00
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2023-07-20 19:32:15 +02:00
} ,
error : function ( jqXHR , exception ) {
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
console . log ( 'Error! Either a file with the same name already existed, or the image file provided was in an invalid format. Double check that the image is not a webp.' ) ;
toastr . error ( 'Something went wrong while saving the character, or the image file provided was in an invalid format. Double check that the image is not a webp.' ) ;
} ,
} ) ;
}
}
window [ "SillyTavern" ] . getContext = function ( ) {
return {
chat : chat ,
characters : characters ,
groups : groups ,
name1 : name1 ,
name2 : name2 ,
characterId : this _chid ,
groupId : selected _group ,
chatId : selected _group
? groups . find ( x => x . id == selected _group ) ? . chat _id
: ( this _chid && characters [ this _chid ] && characters [ this _chid ] . chat ) ,
onlineStatus : online _status ,
maxContext : Number ( max _context ) ,
chatMetadata : chat _metadata ,
streamingProcessor ,
eventSource : eventSource ,
event _types : event _types ,
addOneMessage : addOneMessage ,
generate : Generate ,
getTokenCount : getTokenCount ,
extensionPrompts : extension _prompts ,
setExtensionPrompt : setExtensionPrompt ,
updateChatMetadata : updateChatMetadata ,
saveChat : saveChatConditional ,
saveMetadata : saveMetadata ,
sendSystemMessage : sendSystemMessage ,
activateSendButtons ,
deactivateSendButtons ,
saveReply ,
registerSlashCommand : registerSlashCommand ,
2023-08-22 16:46:37 +02:00
registerHelper : registerExtensionHelper ,
2023-08-27 22:20:43 +02:00
registedDebugFunction : registerDebugFunction ,
2023-07-20 19:32:15 +02:00
} ;
} ;
function swipe _left ( ) { // when we swipe left..but no generation.
if ( chat . length - 1 === Number ( this _edit _mes _id ) ) {
closeMessageEditor ( ) ;
}
if ( isStreamingEnabled ( ) && streamingProcessor ) {
streamingProcessor . isStopped = true ;
}
const swipe _duration = 120 ;
const swipe _range = '700px' ;
chat [ chat . length - 1 ] [ 'swipe_id' ] -- ;
2023-09-07 15:45:19 +02:00
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] < 0 ) {
chat [ chat . length - 1 ] [ 'swipe_id' ] = chat [ chat . length - 1 ] [ 'swipes' ] . length - 1 ;
}
2023-07-20 19:32:15 +02:00
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] >= 0 ) {
/ * $ ( t h i s ) . p a r e n t ( ) . c h i l d r e n ( ' s w i p e _ r i g h t ' ) . c s s ( ' d i s p l a y ' , ' f l e x ' ) ;
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] === 0 ) {
$ ( this ) . css ( 'display' , 'none' ) ;
} * / / / Just in case
if ( ! Array . isArray ( chat [ chat . length - 1 ] [ 'swipe_info' ] ) ) {
chat [ chat . length - 1 ] [ 'swipe_info' ] = [ ] ;
}
let this _mes _div = $ ( this ) . parent ( ) ;
let this _mes _block = $ ( this ) . parent ( ) . children ( '.mes_block' ) . children ( '.mes_text' ) ;
const this _mes _div _height = this _mes _div [ 0 ] . scrollHeight ;
this _mes _div . css ( 'height' , this _mes _div _height ) ;
const this _mes _block _height = this _mes _block [ 0 ] . scrollHeight ;
chat [ chat . length - 1 ] [ 'mes' ] = chat [ chat . length - 1 ] [ 'swipes' ] [ chat [ chat . length - 1 ] [ 'swipe_id' ] ] ;
chat [ chat . length - 1 ] [ 'send_date' ] = chat [ chat . length - 1 ] . swipe _info [ chat [ chat . length - 1 ] [ 'swipe_id' ] ] ? . send _date || chat [ chat . length - 1 ] . send _date ; //load the last mes box with the latest generation
chat [ chat . length - 1 ] [ 'extra' ] = JSON . parse ( JSON . stringify ( chat [ chat . length - 1 ] . swipe _info [ chat [ chat . length - 1 ] [ 'swipe_id' ] ] ? . extra || chat [ chat . length - 1 ] . extra ) ) ;
if ( chat [ chat . length - 1 ] . extra ) {
// if message has memory attached - remove it to allow regen
if ( chat [ chat . length - 1 ] . extra . memory ) {
delete chat [ chat . length - 1 ] . extra . memory ;
}
// ditto for display text
if ( chat [ chat . length - 1 ] . extra . display _text ) {
delete chat [ chat . length - 1 ] . extra . display _text ;
}
}
$ ( this ) . parent ( ) . children ( '.mes_block' ) . transition ( {
x : swipe _range ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
const is _animation _scroll = ( $ ( '#chat' ) . scrollTop ( ) >= ( $ ( '#chat' ) . prop ( "scrollHeight" ) - $ ( '#chat' ) . outerHeight ( ) ) - 10 ) ;
//console.log('on left swipe click calling addOneMessage');
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
if ( ! chat [ chat . length - 1 ] . extra ) {
chat [ chat . length - 1 ] . extra = { } ;
}
const swipeMessage = $ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) ;
const tokenCount = getTokenCount ( chat [ chat . length - 1 ] . mes , 0 ) ;
2023-09-05 03:09:35 +02:00
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = tokenCount ;
2023-08-31 16:10:01 +02:00
swipeMessage . find ( '.tokenCounterDisplay' ) . text ( ` ${ tokenCount } t ` ) ;
}
2023-07-20 19:32:15 +02:00
let new _height = this _mes _div _height - ( this _mes _block _height - this _mes _block [ 0 ] . scrollHeight ) ;
if ( new _height < 103 ) new _height = 103 ;
this _mes _div . animate ( { height : new _height + 'px' } , {
duration : 0 , //used to be 100
queue : false ,
progress : function ( ) {
// Scroll the chat down as the message expands
if ( is _animation _scroll ) $ ( "#chat" ) . scrollTop ( $ ( "#chat" ) [ 0 ] . scrollHeight ) ;
} ,
complete : function ( ) {
this _mes _div . css ( 'height' , 'auto' ) ;
// Scroll the chat down to the bottom once the animation is complete
if ( is _animation _scroll ) $ ( "#chat" ) . scrollTop ( $ ( "#chat" ) [ 0 ] . scrollHeight ) ;
}
} ) ;
$ ( this ) . parent ( ) . children ( '.mes_block' ) . transition ( {
x : '-' + swipe _range ,
duration : 0 ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
$ ( this ) . parent ( ) . children ( '.mes_block' ) . transition ( {
x : '0px' ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : async function ( ) {
await eventSource . emit ( event _types . MESSAGE _SWIPED , ( chat . length - 1 ) ) ;
2023-09-07 15:45:19 +02:00
saveChatDebounced ( ) ;
2023-07-20 19:32:15 +02:00
}
} ) ;
}
} ) ;
}
} ) ;
$ ( this ) . parent ( ) . children ( '.avatar' ) . transition ( {
x : swipe _range ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
$ ( this ) . parent ( ) . children ( '.avatar' ) . transition ( {
x : '-' + swipe _range ,
duration : 0 ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
$ ( this ) . parent ( ) . children ( '.avatar' ) . transition ( {
x : '0px' ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
}
} ) ;
}
} ) ;
}
} ) ;
}
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] < 0 ) {
chat [ chat . length - 1 ] [ 'swipe_id' ] = 0 ;
}
}
// when we click swipe right button
const swipe _right = ( ) => {
if ( chat . length - 1 === Number ( this _edit _mes _id ) ) {
closeMessageEditor ( ) ;
}
if ( isHordeGenerationNotAllowed ( ) ) {
return ;
}
2023-09-07 15:45:19 +02:00
// if (chat.length == 1) {
// if (chat[0]['swipe_id'] !== undefined && chat[0]['swipe_id'] == chat[0]['swipes'].length - 1) {
// toastr.info('Add more alternative greetings to swipe through', 'That\'s all for now');
// return;
// }
// }
2023-07-20 19:32:15 +02:00
const swipe _duration = 200 ;
const swipe _range = 700 ;
//console.log(swipe_range);
let run _generate = false ;
let run _swipe _right = false ;
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] === undefined ) { // if there is no swipe-message in the last spot of the chat array
chat [ chat . length - 1 ] [ 'swipe_id' ] = 0 ; // set it to id 0
chat [ chat . length - 1 ] [ 'swipes' ] = [ ] ; // empty the array
chat [ chat . length - 1 ] [ 'swipe_info' ] = [ ] ;
chat [ chat . length - 1 ] [ 'swipes' ] [ 0 ] = chat [ chat . length - 1 ] [ 'mes' ] ; //assign swipe array with last message from chat
chat [ chat . length - 1 ] [ 'swipe_info' ] [ 0 ] = { 'send_date' : chat [ chat . length - 1 ] [ 'send_date' ] , 'gen_started' : chat [ chat . length - 1 ] [ 'gen_started' ] , 'gen_finished' : chat [ chat . length - 1 ] [ 'gen_finished' ] , 'extra' : JSON . parse ( JSON . stringify ( chat [ chat . length - 1 ] [ 'extra' ] ) ) } ;
//assign swipe info array with last message from chat
}
2023-09-07 15:45:19 +02:00
if ( chat . length === 1 && chat [ 0 ] [ 'swipe_id' ] !== undefined && chat [ 0 ] [ 'swipe_id' ] === chat [ 0 ] [ 'swipes' ] . length - 1 ) { // if swipe_right is called on the last alternate greeting, loop back around
chat [ 0 ] [ 'swipe_id' ] = 0 ;
} else {
chat [ chat . length - 1 ] [ 'swipe_id' ] ++ ; // make new slot in array
}
2023-07-20 19:32:15 +02:00
if ( chat [ chat . length - 1 ] . extra ) {
// if message has memory attached - remove it to allow regen
if ( chat [ chat . length - 1 ] . extra . memory ) {
delete chat [ chat . length - 1 ] . extra . memory ;
}
// ditto for display text
if ( chat [ chat . length - 1 ] . extra . display _text ) {
delete chat [ chat . length - 1 ] . extra . display _text ;
}
}
if ( ! Array . isArray ( chat [ chat . length - 1 ] [ 'swipe_info' ] ) ) {
chat [ chat . length - 1 ] [ 'swipe_info' ] = [ ] ;
}
//console.log(chat[chat.length-1]['swipes']);
2023-09-07 15:45:19 +02:00
if ( parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) === chat [ chat . length - 1 ] [ 'swipes' ] . length && chat . length !== 1 ) { //if swipe id of last message is the same as the length of the 'swipes' array and not the greeting
2023-07-20 19:32:15 +02:00
delete chat [ chat . length - 1 ] . gen _started ;
delete chat [ chat . length - 1 ] . gen _finished ;
run _generate = true ;
} else if ( parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) < chat [ chat . length - 1 ] [ 'swipes' ] . length ) { //otherwise, if the id is less than the number of swipes
chat [ chat . length - 1 ] [ 'mes' ] = chat [ chat . length - 1 ] [ 'swipes' ] [ chat [ chat . length - 1 ] [ 'swipe_id' ] ] ; //load the last mes box with the latest generation
chat [ chat . length - 1 ] [ 'send_date' ] = chat [ chat . length - 1 ] ? . swipe _info [ chat [ chat . length - 1 ] [ 'swipe_id' ] ] ? . send _date || chat [ chat . length - 1 ] [ 'send_date' ] ; //update send date
chat [ chat . length - 1 ] [ 'extra' ] = JSON . parse ( JSON . stringify ( chat [ chat . length - 1 ] . swipe _info [ chat [ chat . length - 1 ] [ 'swipe_id' ] ] ? . extra || chat [ chat . length - 1 ] . extra || [ ] ) ) ;
run _swipe _right = true ; //then prepare to do normal right swipe to show next message
}
const currentMessage = $ ( "#chat" ) . children ( ) . filter ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) ;
let this _div = currentMessage . children ( '.swipe_right' ) ;
let this _mes _div = this _div . parent ( ) ;
if ( chat [ chat . length - 1 ] [ 'swipe_id' ] > chat [ chat . length - 1 ] [ 'swipes' ] . length ) { //if we swipe right while generating (the swipe ID is greater than what we are viewing now)
chat [ chat . length - 1 ] [ 'swipe_id' ] = chat [ chat . length - 1 ] [ 'swipes' ] . length ; //show that message slot (will be '...' while generating)
}
if ( run _generate ) { //hide swipe arrows while generating
this _div . css ( 'display' , 'none' ) ;
}
// handles animated transitions when swipe right, specifically height transitions between messages
if ( run _generate || run _swipe _right ) {
let this _mes _block = this _mes _div . children ( '.mes_block' ) . children ( '.mes_text' ) ;
const this _mes _div _height = this _mes _div [ 0 ] . scrollHeight ;
const this _mes _block _height = this _mes _block [ 0 ] . scrollHeight ;
this _mes _div . children ( '.swipe_left' ) . css ( 'display' , 'flex' ) ;
this _mes _div . children ( '.mes_block' ) . transition ( { // this moves the div back and forth
x : '-' + swipe _range ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
/ * i f ( ! s e l e c t e d _ g r o u p ) {
var typingIndicator = $ ( "#typing_indicator_template .typing_indicator" ) . clone ( ) ;
typingIndicator . find ( ".typing_indicator_name" ) . text ( characters [ this _chid ] . name ) ;
} * /
/* $("#chat").append(typingIndicator); */
const is _animation _scroll = ( $ ( '#chat' ) . scrollTop ( ) >= ( $ ( '#chat' ) . prop ( "scrollHeight" ) - $ ( '#chat' ) . outerHeight ( ) ) - 10 ) ;
//console.log(parseInt(chat[chat.length-1]['swipe_id']));
//console.log(chat[chat.length-1]['swipes'].length);
2023-08-31 16:10:01 +02:00
const swipeMessage = $ ( "#chat" ) . find ( '[mesid="' + ( count _view _mes - 1 ) + '"]' ) ;
2023-07-20 19:32:15 +02:00
if ( run _generate && parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) === chat [ chat . length - 1 ] [ 'swipes' ] . length ) {
2023-08-31 16:10:01 +02:00
//shows "..." while generating
swipeMessage . find ( '.mes_text' ) . html ( '...' ) ;
// resets the timer
swipeMessage . find ( '.mes_timer' ) . html ( '' ) ;
swipeMessage . find ( '.tokenCounterDisplay' ) . text ( '' ) ;
2023-07-20 19:32:15 +02:00
} else {
//console.log('showing previously generated swipe candidate, or "..."');
//console.log('onclick right swipe calling addOneMessage');
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
2023-08-31 16:10:01 +02:00
if ( power _user . message _token _count _enabled ) {
if ( ! chat [ chat . length - 1 ] . extra ) {
chat [ chat . length - 1 ] . extra = { } ;
}
const tokenCount = getTokenCount ( chat [ chat . length - 1 ] . mes , 0 ) ;
2023-09-05 03:09:35 +02:00
chat [ chat . length - 1 ] [ 'extra' ] [ 'token_count' ] = tokenCount ;
2023-08-31 16:10:01 +02:00
swipeMessage . find ( '.tokenCounterDisplay' ) . text ( ` ${ tokenCount } t ` ) ;
}
2023-07-20 19:32:15 +02:00
}
let new _height = this _mes _div _height - ( this _mes _block _height - this _mes _block [ 0 ] . scrollHeight ) ;
if ( new _height < 103 ) new _height = 103 ;
this _mes _div . animate ( { height : new _height + 'px' } , {
duration : 0 , //used to be 100
queue : false ,
progress : function ( ) {
// Scroll the chat down as the message expands
if ( is _animation _scroll ) $ ( "#chat" ) . scrollTop ( $ ( "#chat" ) [ 0 ] . scrollHeight ) ;
} ,
complete : function ( ) {
this _mes _div . css ( 'height' , 'auto' ) ;
// Scroll the chat down to the bottom once the animation is complete
if ( is _animation _scroll ) $ ( "#chat" ) . scrollTop ( $ ( "#chat" ) [ 0 ] . scrollHeight ) ;
}
} ) ;
this _mes _div . children ( '.mes_block' ) . transition ( {
x : swipe _range ,
duration : 0 ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
this _mes _div . children ( '.mes_block' ) . transition ( {
x : '0px' ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : async function ( ) {
await eventSource . emit ( event _types . MESSAGE _SWIPED , ( chat . length - 1 ) ) ;
if ( run _generate && ! is _send _press && parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) === chat [ chat . length - 1 ] [ 'swipes' ] . length ) {
console . debug ( 'caught here 2' ) ;
is _send _press = true ;
$ ( '.mes_buttons:last' ) . hide ( ) ;
await Generate ( 'swipe' ) ;
} else {
if ( parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) !== chat [ chat . length - 1 ] [ 'swipes' ] . length ) {
2023-09-07 15:45:19 +02:00
saveChatDebounced ( ) ;
2023-07-20 19:32:15 +02:00
}
}
}
} ) ;
}
} ) ;
}
} ) ;
this _mes _div . children ( '.avatar' ) . transition ( { // moves avatar along with swipe
x : '-' + swipe _range ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
this _mes _div . children ( '.avatar' ) . transition ( {
x : swipe _range ,
duration : 0 ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
this _mes _div . children ( '.avatar' ) . transition ( {
x : '0px' ,
duration : swipe _duration ,
easing : animation _easing ,
queue : false ,
complete : function ( ) {
}
} ) ;
}
} ) ;
}
} ) ;
}
}
function displayOverrideWarnings ( ) {
if ( ! this _chid || ! selected _group ) {
$ ( '.prompt_overridden' ) . hide ( ) ;
$ ( '.jailbreak_overridden' ) . hide ( ) ;
return ;
}
$ ( '.prompt_overridden' ) . toggle ( ! ! ( characters [ this _chid ] ? . data ? . system _prompt ) ) ;
$ ( '.jailbreak_overridden' ) . toggle ( ! ! ( characters [ this _chid ] ? . data ? . post _history _instructions ) ) ;
}
function connectAPISlash ( _ , text ) {
if ( ! text ) return ;
const apiMap = {
'kobold' : {
button : '#api_button' ,
} ,
'horde' : {
selected : 'koboldhorde' ,
} ,
'novel' : {
button : '#api_button_novel' ,
} ,
'ooba' : {
button : '#api_button_textgenerationwebui' ,
} ,
'oai' : {
selected : 'openai' ,
source : 'openai' ,
button : '#api_button_openai' ,
} ,
'claude' : {
selected : 'openai' ,
source : 'claude' ,
button : '#api_button_openai' ,
} ,
'windowai' : {
selected : 'openai' ,
source : 'windowai' ,
button : '#api_button_openai' ,
} ,
2023-07-26 20:22:57 +02:00
'openrouter' : {
selected : 'openai' ,
source : 'openrouter' ,
button : '#api_button_openai' ,
} ,
2023-08-19 17:20:42 +02:00
'ai21' : {
selected : 'openai' ,
source : 'ai21' ,
button : '#api_button_openai' ,
}
2023-07-20 19:32:15 +02:00
} ;
const apiConfig = apiMap [ text ] ;
if ( ! apiConfig ) {
toastr . error ( ` Error: ${ text } is not a valid API ` ) ;
return ;
}
2023-07-26 20:22:57 +02:00
$ ( ` #main_api option[value=' ${ apiConfig . selected || text } '] ` ) . prop ( "selected" , true ) ;
2023-07-20 19:32:15 +02:00
$ ( "#main_api" ) . trigger ( 'change' ) ;
if ( apiConfig . source ) {
2023-07-26 20:22:57 +02:00
$ ( ` #chat_completion_source option[value=' ${ apiConfig . source } '] ` ) . prop ( "selected" , true ) ;
2023-07-20 19:32:15 +02:00
$ ( "#chat_completion_source" ) . trigger ( 'change' ) ;
}
if ( apiConfig . button ) {
$ ( apiConfig . button ) . trigger ( 'click' ) ;
}
toastr . info ( ` API set to ${ text } , trying to connect.. ` ) ;
}
2023-09-04 19:09:47 +02:00
export function processDroppedFiles ( files ) {
const allowedMimeTypes = [
'application/json' ,
'image/png' ,
'image/webp' ,
] ;
for ( const file of files ) {
if ( allowedMimeTypes . includes ( file . type ) ) {
importCharacter ( file ) ;
} else {
toastr . warning ( 'Unsupported file type: ' + file . name ) ;
}
}
}
2023-07-20 19:32:15 +02:00
function importCharacter ( file ) {
const ext = file . name . match ( /\.(\w+)$/ ) ;
if (
! ext ||
( ext [ 1 ] . toLowerCase ( ) != "json" && ext [ 1 ] . toLowerCase ( ) != "png" && ext [ 1 ] != "webp" )
) {
return ;
}
const format = ext [ 1 ] . toLowerCase ( ) ;
$ ( "#character_import_file_type" ) . val ( format ) ;
const formData = new FormData ( ) ;
formData . append ( 'avatar' , file ) ;
formData . append ( 'file_type' , format ) ;
jQuery . ajax ( {
type : "POST" ,
url : "/importcharacter" ,
data : formData ,
async : false ,
beforeSend : function ( ) {
} ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( data ) {
2023-08-01 17:13:50 +02:00
if ( data . error ) {
toastr . error ( 'The file is likely invalid or corrupted.' , 'Could not import character' ) ;
return ;
}
2023-07-20 19:32:15 +02:00
if ( data . file _name !== undefined ) {
$ ( '#character_search_bar' ) . val ( '' ) . trigger ( 'input' ) ;
let oldSelectedChar = null ;
if ( this _chid != undefined && this _chid != "invalid-safety-id" ) {
oldSelectedChar = characters [ this _chid ] . avatar ;
}
await getCharacters ( ) ;
select _rm _info ( ` char_import ` , data . file _name , oldSelectedChar ) ;
if ( power _user . import _card _tags ) {
let currentContext = getContext ( ) ;
let avatarFileName = ` ${ data . file _name } .png ` ;
let importedCharacter = currentContext . characters . find ( character => character . avatar === avatarFileName ) ;
await importTags ( importedCharacter ) ;
}
}
} ,
error : function ( jqXHR , exception ) {
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
} ,
} ) ;
}
async function importFromURL ( items , files ) {
for ( const item of items ) {
if ( item . type === 'text/uri-list' ) {
const uriList = await new Promise ( ( resolve ) => {
item . getAsString ( ( uriList ) => { resolve ( uriList ) ; } ) ;
} ) ;
const uris = uriList . split ( '\n' ) . filter ( uri => uri . trim ( ) !== '' ) ;
try {
for ( const uri of uris ) {
const request = await fetch ( uri ) ;
const data = await request . blob ( ) ;
const fileName = request . headers . get ( 'Content-Disposition' ) ? . split ( 'filename=' ) [ 1 ] ? . replace ( /"/g , '' ) || uri . split ( '/' ) . pop ( ) || 'file.png' ;
const file = new File ( [ data ] , fileName , { type : data . type } ) ;
files . push ( file ) ;
}
} catch ( error ) {
console . error ( 'Failed to import from URL' , error ) ;
}
}
}
}
async function doImpersonate ( ) {
$ ( '#send_textarea' ) . val ( '' ) ;
$ ( "#option_impersonate" ) . trigger ( 'click' , { fromSlashCommand : true } )
}
async function doDeleteChat ( ) {
$ ( "#option_select_chat" ) . trigger ( 'click' , { fromSlashCommand : true } )
await delay ( 100 )
let currentChatDeleteButton = $ ( ".select_chat_block[highlight='true']" ) . parent ( ) . find ( '.PastChat_cross' )
$ ( currentChatDeleteButton ) . trigger ( 'click' , { fromSlashCommand : true } )
await delay ( 1 )
$ ( "#dialogue_popup_ok" ) . trigger ( 'click' )
//200 delay needed let the past chat view reshow first
await delay ( 200 )
$ ( "#select_chat_cross" ) . trigger ( 'click' )
}
const isPwaMode = window . navigator . standalone ;
if ( isPwaMode ) { $ ( "body" ) . addClass ( 'PWA' ) }
function doCharListDisplaySwitch ( ) {
console . debug ( 'toggling body charListGrid state' )
$ ( "body" ) . toggleClass ( 'charListGrid' )
power _user . charListGrid = $ ( "body" ) . hasClass ( "charListGrid" ) ? true : false ;
2023-08-18 22:13:15 +02:00
saveSettingsDebounced ( ) ;
2023-07-20 19:32:15 +02:00
}
2023-08-03 06:21:38 +02:00
function doCloseChat ( ) {
$ ( "#option_close_chat" ) . trigger ( 'click' )
}
2023-07-24 21:05:27 +02:00
/ * *
* Function to handle the deletion of a character , given a specific popup type and character ID .
* If popup type equals "del_ch" , it will proceed with deletion otherwise it will exit the function .
2023-07-26 20:08:25 +02:00
* It fetches the delete character route , sending necessary parameters , and in case of success ,
2023-07-24 21:05:27 +02:00
* it proceeds to delete character from UI and saves settings .
* In case of error during the fetch request , it logs the error details .
2023-07-26 20:08:25 +02:00
*
2023-07-24 21:05:27 +02:00
* @ param { string } popup _type - The type of popup currently active .
* @ param { string } this _chid - The character ID to be deleted .
2023-08-04 13:41:00 +02:00
* @ param { boolean } delete _chats - Whether to delete chats or not .
2023-07-24 21:05:27 +02:00
* /
2023-08-04 13:41:00 +02:00
export async function handleDeleteCharacter ( popup _type , this _chid , delete _chats ) {
2023-07-24 21:05:27 +02:00
if ( popup _type !== "del_ch" ) {
return ;
}
const avatar = characters [ this _chid ] . avatar ;
const name = characters [ this _chid ] . name ;
2023-09-09 21:15:47 +02:00
const pastChats = await getPastCharacterChats ( ) ;
2023-07-24 21:05:27 +02:00
const msg = { avatar _url : avatar , delete _chats : delete _chats } ;
const response = await fetch ( '/deletecharacter' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( msg ) ,
cache : 'no-cache' ,
} ) ;
if ( response . ok ) {
await deleteCharacter ( name , avatar ) ;
2023-09-09 21:15:47 +02:00
if ( delete _chats ) {
for ( const chat of pastChats ) {
const name = chat . file _name . replace ( '.jsonl' , '' ) ;
await eventSource . emit ( event _types . CHAT _DELETED , name ) ;
}
}
2023-07-24 21:05:27 +02:00
} else {
console . error ( 'Failed to delete character: ' , response . status , response . statusText ) ;
}
}
/ * *
* Function to delete a character from UI after character deletion API success .
2023-07-26 20:08:25 +02:00
* It manages necessary UI changes such as closing advanced editing popup , unsetting
* character ID , resetting characters array and chat metadata , deselecting character ' s tab
* panel , removing character name from navigation tabs , clearing chat , removing character ' s
* avatar from tag _map , fetching updated list of characters and updating the ' deleted
2023-07-24 21:05:27 +02:00
* character ' message .
* It also ensures to save the settings after all the operations .
2023-07-26 20:08:25 +02:00
*
2023-07-24 21:05:27 +02:00
* @ param { string } name - The name of the character to be deleted .
* @ param { string } avatar - The avatar URL of the character to be deleted .
* /
export async function deleteCharacter ( name , avatar ) {
$ ( "#character_cross" ) . click ( ) ;
this _chid = "invalid-safety-id" ;
characters . length = 0 ;
name2 = systemUserName ;
chat = [ ... safetychat ] ;
chat _metadata = { } ;
$ ( document . getElementById ( "rm_button_selected_ch" ) ) . children ( "h2" ) . text ( "" ) ;
clearChat ( ) ;
this _chid = undefined ;
delete tag _map [ avatar ] ;
await getCharacters ( ) ;
select _rm _info ( "char_delete" , name ) ;
2023-09-08 11:10:41 +02:00
await printMessages ( ) ;
2023-07-24 21:05:27 +02:00
saveSettingsDebounced ( ) ;
}
2023-08-03 06:21:38 +02:00
function doTogglePanels ( ) {
$ ( "#option_settings" ) . trigger ( 'click' )
}
2023-08-31 19:31:12 +02:00
jQuery ( async function ( ) {
2023-07-20 19:32:15 +02:00
if ( isMobile ( ) === true ) {
console . debug ( 'hiding movingUI and sheldWidth toggles for mobile' )
$ ( "#sheldWidthToggleBlock" ) . hide ( ) ;
$ ( "#movingUIModeCheckBlock" ) . hide ( ) ;
}
registerSlashCommand ( 'dupe' , DupeChar , [ ] , "– duplicates the currently selected character" , true , true ) ;
2023-08-19 17:20:42 +02:00
registerSlashCommand ( 'api' , connectAPISlash , [ ] , "(kobold, horde, novel, ooba, oai, claude, windowai, ai21) – connect to an API" , true , true ) ;
2023-07-20 19:32:15 +02:00
registerSlashCommand ( 'impersonate' , doImpersonate , [ 'imp' ] , "- calls an impersonation response" , true , true ) ;
registerSlashCommand ( 'delchat' , doDeleteChat , [ ] , "- deletes the current chat" , true , true ) ;
2023-08-03 06:21:38 +02:00
registerSlashCommand ( 'closechat' , doCloseChat , [ ] , "- closes the current chat" , true , true ) ;
registerSlashCommand ( 'panels' , doTogglePanels , [ 'togglepanels' ] , "- toggle UI panels on/off" , true , true ) ;
2023-07-20 19:32:15 +02:00
setTimeout ( function ( ) {
$ ( "#groupControlsToggle" ) . trigger ( 'click' ) ;
$ ( "#groupCurrentMemberListToggle .inline-drawer-icon" ) . trigger ( 'click' ) ;
} , 200 ) ;
2023-08-12 12:10:41 +02:00
$ ( '#chat' ) . on ( 'scroll' , async ( ) => {
// if on the start of the chat and has hidden messages
if ( $ ( '#chat' ) . scrollTop ( ) === 0 && $ ( '#chat' ) . children ( '.mes' ) . not ( ':visible' ) . length > 0 ) {
// show next hidden messages
const prevHeight = $ ( '#chat' ) . prop ( 'scrollHeight' ) ;
$ ( '#chat' ) . children ( '.mes' ) . not ( ':visible' ) . slice ( - power _user . lazy _load ) . show ( ) ;
const newHeight = $ ( '#chat' ) . prop ( 'scrollHeight' ) ;
$ ( '#chat' ) . scrollTop ( newHeight - prevHeight ) ;
}
} ) ;
2023-07-20 19:32:15 +02:00
$ ( "#chat" ) . on ( 'mousewheel touchstart' , ( ) => {
scrollLock = true ;
} ) ;
//////////INPUT BAR FOCUS-KEEPING LOGIC/////////////
let S _TAFocused = false ;
let S _TAPreviouslyFocused = false ;
$ ( '#send_textarea' ) . on ( 'focusin focus click' , ( ) => {
S _TAFocused = true ;
S _TAPreviouslyFocused = true ;
} ) ;
$ ( '#send_textarea' ) . on ( 'focusout blur' , ( ) => S _TAFocused = false ) ;
2023-08-24 00:37:44 +02:00
$ ( '#options_button, #send_but, #option_regenerate, #option_continue, #mes_continue' ) . on ( 'click' , ( ) => {
2023-07-20 19:32:15 +02:00
if ( S _TAPreviouslyFocused ) {
$ ( '#send_textarea' ) . focus ( ) ;
S _TAFocused = true ;
}
} ) ;
$ ( document ) . click ( event => {
if ( $ ( ':focus' ) . attr ( 'id' ) !== 'send_textarea' ) {
2023-08-24 00:37:44 +02:00
var validIDs = [ "options_button" , "send_but" , "mes_continue" , "send_textarea" , "option_regenerate" , "option_continue" ] ;
if ( ! validIDs . includes ( $ ( event . target ) . attr ( 'id' ) ) ) {
2023-07-20 19:32:15 +02:00
S _TAFocused = false ;
S _TAPreviouslyFocused = false ;
}
} else {
S _TAFocused = true ;
S _TAPreviouslyFocused = true ;
}
} ) ;
/////////////////
$ ( '#swipes-checkbox' ) . change ( function ( ) {
swipes = ! ! $ ( '#swipes-checkbox' ) . prop ( 'checked' ) ;
if ( swipes ) {
//console.log('toggle change calling showswipebtns');
showSwipeButtons ( ) ;
} else {
hideSwipeButtons ( ) ;
}
saveSettingsDebounced ( ) ;
} ) ;
///// SWIPE BUTTON CLICKS ///////
$ ( document ) . on ( 'click' , '.swipe_right' , swipe _right ) ;
$ ( document ) . on ( 'click' , '.swipe_left' , swipe _left ) ;
$ ( "#character_search_bar" ) . on ( "input" , function ( ) {
2023-08-22 13:30:49 +02:00
const searchValue = String ( $ ( this ) . val ( ) ) . toLowerCase ( ) ;
2023-08-18 22:13:15 +02:00
entitiesFilter . setFilterData ( FILTER _TYPES . SEARCH , searchValue ) ;
2023-07-20 19:32:15 +02:00
} ) ;
2023-08-24 00:37:44 +02:00
$ ( "#mes_continue" ) . on ( 'click' , function ( ) {
$ ( "#option_continue" ) . trigger ( 'click' ) ;
} ) ;
$ ( "#send_but" ) . on ( 'click' , function ( ) {
2023-07-20 19:32:15 +02:00
if ( is _send _press == false ) {
is _send _press = true ;
Generate ( ) ;
}
} ) ;
//menu buttons setup
$ ( "#rm_button_settings" ) . click ( function ( ) {
selected _button = "settings" ;
menu _type = "settings" ;
selectRightMenuWithAnimation ( 'rm_api_block' ) ;
} ) ;
$ ( "#rm_button_characters" ) . click ( function ( ) {
selected _button = "characters" ;
select _rm _characters ( ) ;
} ) ;
$ ( "#rm_button_back" ) . click ( function ( ) {
selected _button = "characters" ;
select _rm _characters ( ) ;
} ) ;
$ ( "#rm_button_create" ) . click ( function ( ) {
selected _button = "create" ;
select _rm _create ( ) ;
} ) ;
$ ( "#rm_button_selected_ch" ) . click ( function ( ) {
if ( selected _group ) {
select _group _chats ( selected _group ) ;
} else {
selected _button = "character_edit" ;
select _selected _character ( this _chid ) ;
}
$ ( "#character_search_bar" ) . val ( "" ) . trigger ( "input" ) ;
} ) ;
2023-08-20 11:37:38 +02:00
$ ( document ) . on ( "click" , ".character_select" , function ( ) {
2023-08-18 22:13:15 +02:00
const id = $ ( this ) . attr ( "chid" ) ;
selectCharacterById ( id ) ;
2023-07-20 19:32:15 +02:00
} ) ;
$ ( document ) . on ( "input" , ".edit_textarea" , function ( ) {
scroll _holder = $ ( "#chat" ) . scrollTop ( ) ;
$ ( this ) . height ( 0 ) . height ( this . scrollHeight ) ;
is _use _scroll _holder = true ;
} ) ;
$ ( "#chat" ) . on ( "scroll" , function ( ) {
if ( is _use _scroll _holder ) {
$ ( "#chat" ) . scrollTop ( scroll _holder ) ;
is _use _scroll _holder = false ;
}
} ) ;
$ ( document ) . on ( "click" , ".mes" , function ( ) {
//when a 'delete message' parent div is clicked
// and we are in delete mode and del_checkbox is visible
if ( ! is _delete _mode || ! $ ( this ) . children ( '.del_checkbox' ) . is ( ':visible' ) ) {
return ;
}
$ ( ".mes" ) . children ( ".del_checkbox" ) . each ( function ( ) {
$ ( this ) . prop ( "checked" , false ) ;
$ ( this ) . parent ( ) . css ( "background" , css _mes _bg ) ;
} ) ;
$ ( this ) . css ( "background" , "#600" ) ; //sets the bg of the mes selected for deletion
2023-08-22 13:30:49 +02:00
var i = Number ( $ ( this ) . attr ( "mesid" ) ) ; //checks the message ID in the chat
2023-07-20 19:32:15 +02:00
this _del _mes = i ;
while ( i < chat . length ) {
//as long as the current message ID is less than the total chat length
$ ( ".mes[mesid='" + i + "']" ) . css ( "background" , "#600" ) ; //sets the bg of the all msgs BELOW the selected .mes
$ ( ".mes[mesid='" + i + "']" )
. children ( ".del_checkbox" )
. prop ( "checked" , true ) ;
i ++ ;
//console.log(i);
}
} ) ;
$ ( document ) . on ( "click" , "#user_avatar_block .avatar" , setUserAvatar ) ;
$ ( document ) . on ( "click" , "#user_avatar_block .avatar_upload" , function ( ) {
$ ( "#avatar_upload_overwrite" ) . val ( "" ) ;
$ ( "#avatar_upload_file" ) . trigger ( 'click' ) ;
} ) ;
$ ( document ) . on ( "click" , "#user_avatar_block .set_persona_image" , function ( ) {
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . log ( 'no imgfile' ) ;
return ;
}
$ ( "#avatar_upload_overwrite" ) . val ( avatarId ) ;
$ ( "#avatar_upload_file" ) . trigger ( 'click' ) ;
} ) ;
$ ( "#avatar_upload_file" ) . on ( "change" , uploadUserAvatar ) ;
$ ( document ) . on ( "click" , ".bg_example" , async function ( ) {
//when user clicks on a BG thumbnail...
const this _bgfile = $ ( this ) . attr ( "bgfile" ) ; // this_bgfile = whatever they clicked
const customBg = window . getComputedStyle ( document . getElementById ( 'bg_custom' ) ) . backgroundImage ;
// custom background is set. Do not override the layer below
if ( customBg !== 'none' ) {
return ;
}
// if clicked on upload button
if ( ! this _bgfile ) {
return ;
}
const backgroundUrl = ` backgrounds/ ${ this _bgfile } ` ;
// fetching to browser memory to reduce flicker
fetch ( backgroundUrl ) . then ( ( ) => {
$ ( "#bg1" ) . css (
"background-image" ,
` url(" ${ backgroundUrl } ") `
) ;
setBackground ( this _bgfile ) ;
} ) . catch ( ( ) => {
console . log ( 'Background could not be set: ' + backgroundUrl ) ;
} ) ;
} ) ;
$ ( document ) . on ( 'click' , '.bg_example_edit' , async function ( e ) {
e . stopPropagation ( ) ;
const old _bg = $ ( this ) . attr ( 'bgfile' ) ;
if ( ! old _bg ) {
console . debug ( 'no bgfile' ) ;
return ;
}
const fileExtension = old _bg . split ( '.' ) . pop ( ) ;
const old _bg _extensionless = old _bg . replace ( ` . ${ fileExtension } ` , '' ) ;
const new _bg _extensionless = await callPopup ( '<h3>Enter new background name:</h3>' , 'input' , old _bg _extensionless ) ;
2023-07-21 11:00:40 +02:00
if ( ! new _bg _extensionless ) {
console . debug ( 'no new_bg_extensionless' ) ;
return ;
}
2023-07-20 19:32:15 +02:00
const new _bg = ` ${ new _bg _extensionless } . ${ fileExtension } ` ;
if ( old _bg _extensionless === new _bg _extensionless ) {
console . debug ( 'new_bg === old_bg' ) ;
return ;
}
const data = { old _bg , new _bg } ;
const response = await fetch ( '/renamebackground' , {
method : 'POST' ,
2023-07-27 23:38:43 +02:00
headers : getRequestHeaders ( ) ,
2023-07-20 19:32:15 +02:00
body : JSON . stringify ( data ) ,
cache : 'no-cache' ,
} ) ;
if ( response . ok ) {
await getBackgrounds ( ) ;
} else {
toastr . warning ( 'Failed to rename background' ) ;
}
} ) ;
$ ( document ) . on ( "click" , ".bg_example_cross" , function ( e ) {
e . stopPropagation ( ) ;
bg _file _for _del = $ ( this ) ;
//$(this).parent().remove();
//delBackground(this_bgfile);
popup _type = "del_bg" ;
callPopup ( "<h3>Delete the background?</h3>" ) ;
} ) ;
$ ( document ) . on ( "click" , ".PastChat_cross" , function ( ) {
chat _file _for _del = $ ( this ) . attr ( 'file_name' ) ;
console . debug ( 'detected cross click for' + chat _file _for _del ) ;
popup _type = "del_chat" ;
callPopup ( "<h3>Delete the Chat File?</h3>" ) ;
} ) ;
$ ( "#advanced_div" ) . click ( function ( ) {
if ( ! is _advanced _char _open ) {
is _advanced _char _open = true ;
$ ( "#character_popup" ) . css ( "display" , "flex" ) ;
$ ( "#character_popup" ) . css ( "opacity" , 0.0 ) ;
$ ( "#character_popup" ) . transition ( {
opacity : 1.0 ,
duration : animation _duration ,
easing : animation _easing ,
} ) ;
} else {
is _advanced _char _open = false ;
$ ( "#character_popup" ) . css ( "display" , "none" ) ;
}
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( "#character_cross" ) . click ( function ( ) {
is _advanced _char _open = false ;
$ ( "#character_popup" ) . transition ( {
opacity : 0 ,
duration : 200 ,
easing : animation _easing ,
} ) ;
setTimeout ( function ( ) { $ ( "#character_popup" ) . css ( "display" , "none" ) ; } , 200 ) ;
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( "#character_popup_ok" ) . click ( function ( ) {
is _advanced _char _open = false ;
$ ( "#character_popup" ) . css ( "display" , "none" ) ;
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( "#dialogue_popup_ok" ) . click ( async function ( e ) {
$ ( "#shadow_popup" ) . transition ( {
opacity : 0 ,
duration : 200 ,
easing : animation _easing ,
} ) ;
setTimeout ( function ( ) {
$ ( "#shadow_popup" ) . css ( "display" , "none" ) ;
$ ( "#dialogue_popup" ) . removeClass ( 'large_dialogue_popup' ) ;
$ ( "#dialogue_popup" ) . removeClass ( 'wide_dialogue_popup' ) ;
} , 200 ) ;
// $("#shadow_popup").css("opacity:", 0.0);
if ( popup _type == 'avatarToCrop' ) {
dialogueResolve ( $ ( "#avatarToCrop" ) . data ( 'cropper' ) . getCroppedCanvas ( ) . toDataURL ( 'image/jpeg' ) ) ;
} ;
if ( popup _type == "del_bg" ) {
delBackground ( bg _file _for _del . attr ( "bgfile" ) ) ;
bg _file _for _del . parent ( ) . remove ( ) ;
}
if ( popup _type == "del_chat" ) {
//close past chat popup
$ ( "#select_chat_cross" ) . click ( ) ;
if ( selected _group ) {
await deleteGroupChat ( selected _group , chat _file _for _del ) ;
} else {
await delChat ( chat _file _for _del ) ;
}
//open the history view again after 100ms
//hide option popup menu
setTimeout ( function ( ) {
$ ( "#option_select_chat" ) . click ( ) ;
$ ( "#options" ) . hide ( ) ;
2023-08-19 14:58:17 +02:00
} , 2000 ) ;
2023-07-20 19:32:15 +02:00
}
if ( popup _type == "del_ch" ) {
2023-08-04 13:41:00 +02:00
const deleteChats = ! ! $ ( "#del_char_checkbox" ) . prop ( "checked" ) ;
await handleDeleteCharacter ( popup _type , this _chid , deleteChats ) ;
2023-08-18 15:45:40 +02:00
eventSource . emit ( 'characterDeleted' , { id : this _chid , character : characters [ this _chid ] } ) ;
2023-07-20 19:32:15 +02:00
}
if ( popup _type == "alternate_greeting" && menu _type !== "create" ) {
createOrEditCharacter ( ) ;
}
if ( popup _type === "del_group" ) {
const groupId = $ ( "#dialogue_popup" ) . data ( "group_id" ) ;
if ( groupId ) {
deleteGroup ( groupId ) ;
}
}
//Make a new chat for selected character
if (
popup _type == "new_chat" &&
( selected _group || this _chid !== undefined ) &&
menu _type != "create"
) {
//Fix it; New chat doesn't create while open create character menu
clearChat ( ) ;
chat . length = 0 ;
if ( selected _group ) {
2023-08-23 23:54:36 +02:00
await createNewGroupChat ( selected _group ) ;
2023-07-20 19:32:15 +02:00
}
else {
//RossAscends: added character name to new chat filenames and replaced Date.now() with humanizedDateTime;
chat _metadata = { } ;
characters [ this _chid ] . chat = name2 + " - " + humanizedDateTime ( ) ;
$ ( "#selected_chat_pole" ) . val ( characters [ this _chid ] . chat ) ;
2023-08-23 23:54:36 +02:00
await getChat ( ) ;
await createOrEditCharacter ( ) ;
2023-07-20 19:32:15 +02:00
}
}
rawPromptPopper . update ( ) ;
$ ( '#rawPromptPopup' ) . hide ( ) ;
if ( dialogueResolve ) {
if ( popup _type == 'input' ) {
dialogueResolve ( $ ( "#dialogue_popup_input" ) . val ( ) ) ;
$ ( "#dialogue_popup_input" ) . val ( '' ) ;
}
else {
dialogueResolve ( true ) ;
}
dialogueResolve = null ;
}
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( "#dialogue_popup_cancel" ) . click ( function ( e ) {
$ ( "#shadow_popup" ) . transition ( {
opacity : 0 ,
duration : 200 ,
easing : animation _easing ,
} ) ;
setTimeout ( function ( ) {
$ ( "#shadow_popup" ) . css ( "display" , "none" ) ;
$ ( "#dialogue_popup" ) . removeClass ( 'large_dialogue_popup' ) ;
} , 200 ) ;
//$("#shadow_popup").css("opacity:", 0.0);
popup _type = "" ;
if ( dialogueResolve ) {
dialogueResolve ( false ) ;
dialogueResolve = null ;
}
} ) ;
$ ( "#add_bg_button" ) . change ( function ( ) {
read _bg _load ( this ) ;
} ) ;
$ ( "#add_avatar_button" ) . change ( function ( ) {
read _avatar _load ( this ) ;
} ) ;
$ ( "#form_create" ) . submit ( createOrEditCharacter ) ;
$ ( "#delete_button" ) . on ( 'click' , function ( ) {
popup _type = "del_ch" ;
callPopup ( `
< h3 > Delete the character ? < / h 3 >
< b > THIS IS PERMANENT ! < br > < br >
< label for = "del_char_checkbox" class = "checkbox_label justifyCenter" >
2023-08-03 15:39:15 +02:00
< input type = "checkbox" id = "del_char_checkbox" / >
2023-07-20 19:32:15 +02:00
< span > Also delete the chat files < / s p a n >
< / l a b e l > < b r > < / b > `
) ;
} ) ;
//////// OPTIMIZED ALL CHAR CREATION/EDITING TEXTAREA LISTENERS ///////////////
$ ( "#character_name_pole" ) . on ( "input" , function ( ) {
if ( menu _type == "create" ) {
create _save . name = $ ( "#character_name_pole" ) . val ( ) ;
}
} ) ;
const elementsToUpdate = {
'#description_textarea' : function ( ) { create _save . description = $ ( "#description_textarea" ) . val ( ) ; } ,
'#creator_notes_textarea' : function ( ) { create _save . creator _notes = $ ( "#creator_notes_textarea" ) . val ( ) ; } ,
'#character_version_textarea' : function ( ) { create _save . character _version = $ ( "#character_version_textarea" ) . val ( ) ; } ,
'#system_prompt_textarea' : function ( ) { create _save . system _prompt = $ ( "#system_prompt_textarea" ) . val ( ) ; } ,
'#post_history_instructions_textarea' : function ( ) { create _save . post _history _instructions = $ ( "#post_history_instructions_textarea" ) . val ( ) ; } ,
'#creator_textarea' : function ( ) { create _save . creator = $ ( "#creator_textarea" ) . val ( ) ; } ,
'#tags_textarea' : function ( ) { create _save . tags = $ ( "#tags_textarea" ) . val ( ) ; } ,
'#personality_textarea' : function ( ) { create _save . personality = $ ( "#personality_textarea" ) . val ( ) ; } ,
'#scenario_pole' : function ( ) { create _save . scenario = $ ( "#scenario_pole" ) . val ( ) ; } ,
'#mes_example_textarea' : function ( ) { create _save . mes _example = $ ( "#mes_example_textarea" ) . val ( ) ; } ,
'#firstmessage_textarea' : function ( ) { create _save . first _message = $ ( "#firstmessage_textarea" ) . val ( ) ; } ,
'#talkativeness_slider' : function ( ) { create _save . talkativeness = $ ( "#talkativeness_slider" ) . val ( ) ; } ,
} ;
Object . keys ( elementsToUpdate ) . forEach ( function ( id ) {
$ ( id ) . on ( "input" , function ( ) {
if ( menu _type == "create" ) {
elementsToUpdate [ id ] ( ) ;
} else {
saveCharacterDebounced ( ) ;
}
} ) ;
} ) ;
$ ( "#favorite_button" ) . on ( 'click' , function ( ) {
updateFavButtonState ( ! fav _ch _checked ) ;
if ( menu _type != "create" ) {
saveCharacterDebounced ( ) ;
}
} ) ;
/* $("#renameCharButton").on('click', renameCharacter); */
$ ( document ) . on ( "click" , ".renameChatButton" , async function ( ) {
const old _filenamefull = $ ( this ) . closest ( '.select_chat_block_wrapper' ) . find ( '.select_chat_block_filename' ) . text ( ) ;
const old _filename = old _filenamefull . replace ( '.jsonl' , '' ) ;
const popupText = ` <h3>Enter the new name for the chat:<h3>
< small > ! ! Using an existing filename will produce an error ! ! < br >
This will break the link between bookmark chats . < br >
No need to add '.jsonl' at the end . < br >
< / s m a l l > ` ;
const newName = await callPopup ( popupText , 'input' , old _filename ) ;
if ( ! newName || newName == old _filename ) {
console . log ( 'no new name found, aborting' ) ;
return ;
}
const body = {
is _group : ! ! selected _group ,
avatar _url : characters [ this _chid ] ? . avatar ,
original _file : ` ${ old _filename } .jsonl ` ,
renamed _file : ` ${ newName } .jsonl ` ,
}
try {
const response = await fetch ( '/renamechat' , {
method : 'POST' ,
body : JSON . stringify ( body ) ,
headers : getRequestHeaders ( ) ,
} ) ;
if ( ! response . ok ) {
throw new Error ( 'Unsuccessful request.' ) ;
}
const data = response . json ( ) ;
if ( data . error ) {
throw new Error ( 'Server returned an error.' ) ;
}
if ( selected _group ) {
await renameGroupChat ( selected _group , old _filename , newName ) ;
}
else {
if ( characters [ this _chid ] . chat == old _filename ) {
characters [ this _chid ] . chat = newName ;
2023-08-23 20:32:38 +02:00
await createOrEditCharacter ( ) ;
2023-07-20 19:32:15 +02:00
}
}
2023-08-23 20:32:38 +02:00
await reloadCurrentChat ( ) ;
2023-07-20 19:32:15 +02:00
await delay ( 250 ) ;
$ ( "#option_select_chat" ) . trigger ( 'click' ) ;
$ ( "#options" ) . hide ( ) ;
} catch {
await delay ( 500 ) ;
await callPopup ( 'An error has occurred. Chat was not renamed.' , 'text' ) ;
}
} ) ;
$ ( document ) . on ( "click" , ".exportChatButton, .exportRawChatButton" , async function ( ) {
const format = $ ( this ) . data ( 'format' ) || 'txt' ;
await saveChatConditional ( ) ;
const filenamefull = $ ( this ) . closest ( '.select_chat_block_wrapper' ) . find ( '.select_chat_block_filename' ) . text ( ) ;
console . log ( ` exporting ${ filenamefull } in ${ format } format ` ) ;
const filename = filenamefull . replace ( '.jsonl' , '' ) ;
const body = {
is _group : ! ! selected _group ,
avatar _url : characters [ this _chid ] ? . avatar ,
file : ` ${ filename } .jsonl ` ,
exportfilename : ` ${ filename } . ${ format } ` ,
format : format ,
}
console . log ( body ) ;
try {
const response = await fetch ( '/exportchat' , {
method : 'POST' ,
body : JSON . stringify ( body ) ,
headers : getRequestHeaders ( ) ,
} ) ;
const data = await response . json ( ) ;
if ( ! response . ok ) {
// display error message
console . log ( data . message ) ;
await delay ( 250 ) ;
toastr . error ( ` Error: ${ data . message } ` ) ;
return ;
} else {
const mimeType = format == 'txt' ? 'text/plain' : 'application/octet-stream' ;
// success, handle response data
console . log ( data ) ;
await delay ( 250 ) ;
toastr . success ( data . message ) ;
download ( data . result , body . exportfilename , mimeType ) ;
}
} catch ( error ) {
// display error message
console . log ( ` An error has occurred: ${ error . message } ` ) ;
await delay ( 250 ) ;
toastr . error ( ` Error: ${ error . message } ` ) ;
}
} ) ;
///////////////////////////////////////////////////////////////////////////////////
$ ( "#api_button" ) . click ( function ( e ) {
if ( $ ( "#api_url_text" ) . val ( ) != "" ) {
2023-08-22 13:30:49 +02:00
let value = formatKoboldUrl ( String ( $ ( "#api_url_text" ) . val ( ) ) . trim ( ) ) ;
2023-07-20 19:32:15 +02:00
if ( ! value ) {
toastr . error ( 'Please enter a valid URL.' ) ;
return ;
}
$ ( "#api_url_text" ) . val ( value ) ;
api _server = value ;
$ ( "#api_loading" ) . css ( "display" , "inline-block" ) ;
$ ( "#api_button" ) . css ( "display" , "none" ) ;
main _api = "kobold" ;
saveSettingsDebounced ( ) ;
is _get _status = true ;
is _api _button _press = true ;
getStatus ( ) ;
}
} ) ;
2023-08-03 05:25:24 +02:00
$ ( "#use-mancer-api-checkbox" ) . on ( "change" , function ( e ) {
const enabled = $ ( "#use-mancer-api-checkbox" ) . prop ( "checked" ) ;
2023-08-20 06:33:00 +02:00
$ ( "#mancer_api_subpanel" ) . toggle ( enabled ) ;
$ ( "#tgwebui_api_subpanel" ) . toggle ( ! enabled ) ;
2023-08-20 18:09:19 +02:00
2023-08-03 05:25:24 +02:00
api _use _mancer _webui = enabled ;
2023-08-05 14:44:15 +02:00
saveSettingsDebounced ( ) ;
2023-08-03 05:25:24 +02:00
getStatus ( ) ;
2023-09-10 03:08:15 +02:00
if ( enabled ) {
loadMancerModels ( ) ;
}
2023-08-03 05:25:24 +02:00
} ) ;
2023-08-03 12:07:54 +02:00
$ ( "#api_button_textgenerationwebui" ) . click ( async function ( e ) {
2023-08-20 06:33:00 +02:00
const url _source = api _use _mancer _webui ? "#mancer_api_url_text" : "#textgenerationwebui_api_url_text" ;
if ( $ ( url _source ) . val ( ) != "" ) {
2023-08-22 13:30:49 +02:00
let value = formatTextGenURL ( String ( $ ( url _source ) . val ( ) ) . trim ( ) , api _use _mancer _webui ) ;
2023-07-20 19:32:15 +02:00
if ( ! value ) {
2023-08-08 22:12:03 +02:00
callPopup ( "Please enter a valid URL.<br/>WebUI URLs should end with <tt>/api</tt><br/>Enable 'Relaxed API URLs' to allow other paths." , 'text' ) ;
2023-07-20 19:32:15 +02:00
return ;
}
2023-08-22 13:30:49 +02:00
const mancer _key = String ( $ ( "#api_key_mancer" ) . val ( ) ) . trim ( ) ;
2023-08-03 05:25:24 +02:00
if ( mancer _key . length ) {
2023-08-03 12:07:54 +02:00
await writeSecret ( SECRET _KEYS . MANCER , mancer _key ) ;
2023-08-03 05:25:24 +02:00
}
2023-08-20 06:33:00 +02:00
$ ( url _source ) . val ( value ) ;
2023-07-20 19:32:15 +02:00
$ ( "#api_loading_textgenerationwebui" ) . css ( "display" , "inline-block" ) ;
$ ( "#api_button_textgenerationwebui" ) . css ( "display" , "none" ) ;
2023-08-20 06:33:00 +02:00
if ( api _use _mancer _webui ) {
textgenerationwebui _settings . streaming _url = value . replace ( "http" , "ws" ) + "/v1/stream" ;
}
2023-07-20 19:32:15 +02:00
api _server _textgenerationwebui = value ;
main _api = "textgenerationwebui" ;
saveSettingsDebounced ( ) ;
is _get _status = true ;
is _api _button _press = true ;
getStatus ( ) ;
}
} ) ;
var button = $ ( '#options_button' ) ;
var menu = $ ( '#options' ) ;
function showMenu ( ) {
showBookmarksButtons ( ) ;
menu . stop ( ) . fadeIn ( 250 ) ;
optionsPopper . update ( ) ;
}
function hideMenu ( ) {
menu . stop ( ) . fadeOut ( 250 ) ;
optionsPopper . update ( ) ;
}
function isMouseOverButtonOrMenu ( ) {
return menu . is ( ':hover' ) || button . is ( ':hover' ) ;
}
button . on ( 'mouseenter click' , function ( ) { showMenu ( ) ; } ) ;
button . on ( 'mouseleave' , function ( ) {
//delay to prevent menu hiding when mouse leaves button into menu
setTimeout ( ( ) => {
if ( ! isMouseOverButtonOrMenu ( ) ) { hideMenu ( ) ; }
} , 100 )
} ) ;
menu . on ( 'mouseleave' , function ( ) {
//delay to prevent menu hide when mouseleaves menu into button
setTimeout ( ( ) => {
if ( ! isMouseOverButtonOrMenu ( ) ) { hideMenu ( ) ; }
} , 100 )
} ) ;
$ ( document ) . on ( 'click' , function ( ) {
if ( ! isMouseOverButtonOrMenu ( ) && menu . is ( ':visible' ) ) { hideMenu ( ) ; }
} ) ;
/* $('#set_chat_scenario').on('click', setScenarioOverride); */
///////////// OPTIMIZED LISTENERS FOR LEFT SIDE OPTIONS POPUP MENU //////////////////////
$ ( "#options [id]" ) . on ( "click" , function ( event , customData ) {
const fromSlashCommand = customData ? . fromSlashCommand || false ;
var id = $ ( this ) . attr ( "id" ) ;
if ( id == "option_select_chat" ) {
if ( ( selected _group && ! is _group _generating ) || ( this _chid !== undefined && ! is _send _press ) || fromSlashCommand ) {
displayPastChats ( ) ;
//this is just to avoid the shadow for past chat view when using /delchat
//however, the dialog popup still gets one..
if ( ! fromSlashCommand ) {
console . log ( 'displaying shadow' )
$ ( "#shadow_select_chat_popup" ) . css ( "display" , "block" ) ;
$ ( "#shadow_select_chat_popup" ) . css ( "opacity" , 0.0 ) ;
$ ( "#shadow_select_chat_popup" ) . transition ( {
opacity : 1.0 ,
duration : animation _duration ,
easing : animation _easing ,
} ) ;
}
}
}
else if ( id == "option_start_new_chat" ) {
if ( ( selected _group || this _chid !== undefined ) && ! is _send _press ) {
popup _type = "new_chat" ;
callPopup ( "<h3>Start new chat?</h3>" ) ;
}
}
else if ( id == "option_regenerate" ) {
if ( is _send _press == false ) {
//hideSwipeButtons();
if ( selected _group ) {
regenerateGroup ( ) ;
}
else {
is _send _press = true ;
Generate ( "regenerate" ) ;
}
}
}
else if ( id == "option_impersonate" ) {
if ( is _send _press == false || fromSlashCommand ) {
is _send _press = true ;
Generate ( "impersonate" ) ;
}
}
else if ( id == 'option_continue' ) {
if ( is _send _press == false || fromSlashCommand ) {
is _send _press = true ;
Generate ( "continue" ) ;
}
}
else if ( id == "option_delete_mes" ) {
setTimeout ( openMessageDelete , animation _duration ) ;
}
2023-07-30 04:47:17 +02:00
2023-07-30 20:35:21 +02:00
else if ( id == "option_close_chat" ) {
if ( is _send _press == false ) {
clearChat ( ) ;
chat . length = 0 ;
resetSelectedGroup ( ) ;
setCharacterId ( undefined ) ;
setCharacterName ( '' ) ;
setActiveCharacter ( null ) ;
setActiveGroup ( null ) ;
this _edit _mes _id = undefined ;
chat _metadata = { } ;
selected _button = "characters" ;
$ ( "#rm_button_selected_ch" ) . children ( "h2" ) . text ( '' ) ;
select _rm _characters ( ) ;
sendSystemMessage ( system _message _types . WELCOME ) ;
} else {
toastr . info ( "Please stop the message generation first." ) ;
}
}
2023-07-30 04:56:17 +02:00
else if ( id === "option_settings" ) {
2023-07-30 04:47:17 +02:00
//var checkBox = document.getElementById("waifuMode");
var topBar = document . getElementById ( "top-bar" ) ;
var topSettingsHolder = document . getElementById ( "top-settings-holder" ) ;
2023-07-30 04:56:17 +02:00
var divchat = document . getElementById ( "chat" ) ;
2023-07-30 18:08:24 +02:00
2023-07-30 04:47:17 +02:00
//if (checkBox.checked) {
if ( topBar . style . display === "none" ) {
2023-07-30 04:56:17 +02:00
topBar . style . display = "" ; // or "inline-block" if that's the original display value
topSettingsHolder . style . display = "" ; // or "inline-block" if that's the original display value
divchat . style . borderRadius = "" ;
divchat . style . backgroundColor = "" ;
2023-07-30 04:57:21 +02:00
2023-07-30 04:47:17 +02:00
} else {
2023-07-30 18:08:24 +02:00
2023-07-30 04:56:17 +02:00
divchat . style . borderRadius = "10px" ; // Adjust the value to control the roundness of the corners
2023-07-30 18:08:24 +02:00
divchat . style . backgroundColor = "" ; // Set the background color to your preference
2023-07-30 04:56:17 +02:00
topBar . style . display = "none" ;
topSettingsHolder . style . display = "none" ;
2023-07-30 04:47:17 +02:00
}
2023-07-30 04:56:17 +02:00
//}
2023-07-30 04:47:17 +02:00
}
2023-07-20 19:32:15 +02:00
hideMenu ( ) ;
} ) ;
//////////////////////////////////////////////////////////////////////////////////////////////
//functionality for the cancel delete messages button, reverts to normal display of input form
$ ( "#dialogue_del_mes_cancel" ) . click ( function ( ) {
$ ( "#dialogue_del_mes" ) . css ( "display" , "none" ) ;
$ ( "#send_form" ) . css ( "display" , css _send _form _display ) ;
$ ( ".del_checkbox" ) . each ( function ( ) {
$ ( this ) . css ( "display" , "none" ) ;
$ ( this ) . parent ( ) . children ( ".for_checkbox" ) . css ( "display" , "block" ) ;
$ ( this ) . parent ( ) . css ( "background" , css _mes _bg ) ;
$ ( this ) . prop ( "checked" , false ) ;
} ) ;
this _del _mes = 0 ;
console . debug ( 'canceled del msgs, calling showswipesbtns' ) ;
showSwipeButtons ( ) ;
is _delete _mode = false ;
} ) ;
2023-08-05 17:50:30 +02:00
//confirms message deletion with the "ok" button
2023-08-23 09:32:48 +02:00
$ ( "#dialogue_del_mes_ok" ) . click ( async function ( ) {
2023-07-20 19:32:15 +02:00
$ ( "#dialogue_del_mes" ) . css ( "display" , "none" ) ;
$ ( "#send_form" ) . css ( "display" , css _send _form _display ) ;
$ ( ".del_checkbox" ) . each ( function ( ) {
$ ( this ) . css ( "display" , "none" ) ;
$ ( this ) . parent ( ) . children ( ".for_checkbox" ) . css ( "display" , "block" ) ;
$ ( this ) . parent ( ) . css ( "background" , css _mes _bg ) ;
$ ( this ) . prop ( "checked" , false ) ;
} ) ;
if ( this _del _mes != 0 ) {
$ ( ".mes[mesid='" + this _del _mes + "']" )
. nextAll ( "div" )
. remove ( ) ;
$ ( ".mes[mesid='" + this _del _mes + "']" ) . remove ( ) ;
chat . length = this _del _mes ;
count _view _mes = this _del _mes ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
var $textchat = $ ( "#chat" ) ;
$textchat . scrollTop ( $textchat [ 0 ] . scrollHeight ) ;
eventSource . emit ( event _types . MESSAGE _DELETED , chat . length ) ;
}
this _del _mes = 0 ;
$ ( '#chat .mes' ) . last ( ) . addClass ( 'last_mes' ) ;
$ ( '#chat .mes' ) . eq ( - 2 ) . removeClass ( 'last_mes' ) ;
console . debug ( 'confirmed del msgs, calling showswipesbtns' ) ;
showSwipeButtons ( ) ;
is _delete _mode = false ;
} ) ;
$ ( "#settings_perset" ) . change ( function ( ) {
if ( $ ( "#settings_perset" ) . find ( ":selected" ) . val ( ) != "gui" ) {
preset _settings = $ ( "#settings_perset" ) . find ( ":selected" ) . text ( ) ;
const preset = koboldai _settings [ koboldai _setting _names [ preset _settings ] ] ;
loadKoboldSettings ( preset ) ;
2023-07-23 22:52:31 +02:00
setGenerationParamsFromPreset ( preset ) ;
2023-07-20 19:32:15 +02:00
$ ( "#range_block" ) . find ( 'input' ) . prop ( "disabled" , false ) ;
$ ( "#kobold-advanced-config" ) . find ( 'input' ) . prop ( "disabled" , false ) ;
$ ( "#kobold-advanced-config" ) . css ( 'opacity' , 1.0 ) ;
$ ( "#range_block" ) . css ( "opacity" , 1.0 ) ;
$ ( "#amount_gen_block" ) . find ( 'input' ) . prop ( "disabled" , false ) ;
$ ( "#amount_gen_block" ) . css ( "opacity" , 1.0 ) ;
$ ( "#kobold_order" ) . sortable ( "enable" ) ;
} else {
//$('.button').disableSelection();
preset _settings = "gui" ;
$ ( "#range_block" ) . find ( 'input' ) . prop ( "disabled" , true ) ;
2023-09-13 14:19:44 +02:00
$ ( "#range_block" ) . css ( "opacity" , 0.5 ) ;
2023-07-20 19:32:15 +02:00
$ ( "#kobold-advanced-config" ) . find ( 'input' ) . prop ( "disabled" , true ) ;
$ ( "#kobold-advanced-config" ) . css ( 'opacity' , 0.5 ) ;
$ ( "#kobold_order" ) . sortable ( "disable" ) ;
}
saveSettingsDebounced ( ) ;
} ) ;
$ ( "#settings_perset_novel" ) . change ( function ( ) {
nai _settings . preset _settings _novel = $ ( "#settings_perset_novel" )
. find ( ":selected" )
. text ( ) ;
const preset = novelai _settings [ novelai _setting _names [ nai _settings . preset _settings _novel ] ] ;
loadNovelPreset ( preset ) ;
2023-08-22 13:30:49 +02:00
amount _gen = Number ( $ ( "#amount_gen" ) . val ( ) ) ;
max _context = Number ( $ ( "#max_context" ) . val ( ) ) ;
2023-07-20 19:32:15 +02:00
saveSettingsDebounced ( ) ;
} ) ;
$ ( "#main_api" ) . change ( function ( ) {
is _get _status = false ;
is _get _status _novel = false ;
setOpenAIOnlineStatus ( false ) ;
online _status = "no_connection" ;
checkOnlineStatus ( ) ;
changeMainAPI ( ) ;
saveSettingsDebounced ( ) ;
} ) ;
////////////////// OPTIMIZED RANGE SLIDER LISTENERS////////////////
2023-08-23 20:34:20 +02:00
var sliderLocked = true ;
var sliderTimer ;
$ ( "input[type='range']" ) . on ( "touchstart" , function ( ) {
2023-08-25 00:06:52 +02:00
// Unlock the slider after 300ms
setTimeout ( function ( ) {
2023-08-23 20:34:20 +02:00
sliderLocked = false ;
2023-08-25 00:06:52 +02:00
$ ( this ) . css ( 'background-color' , 'var(--SmartThemeQuoteColor)' ) ;
} . bind ( this ) , 300 ) ;
2023-08-23 20:34:20 +02:00
} ) ;
$ ( "input[type='range']" ) . on ( "touchend" , function ( ) {
clearTimeout ( sliderTimer ) ;
2023-08-25 00:06:52 +02:00
$ ( this ) . css ( 'background-color' , '' ) ;
sliderLocked = true ;
2023-08-23 20:34:20 +02:00
} ) ;
$ ( "input[type='range']" ) . on ( "touchmove" , function ( event ) {
if ( sliderLocked ) {
event . preventDefault ( ) ;
}
} ) ;
2023-07-20 19:32:15 +02:00
const sliders = [
{
sliderId : "#amount_gen" ,
counterId : "#amount_gen_counter" ,
format : ( val ) => ` ${ val } ` ,
setValue : ( val ) => { amount _gen = Number ( val ) ; } ,
} ,
{
sliderId : "#max_context" ,
counterId : "#max_context_counter" ,
format : ( val ) => ` ${ val } ` ,
setValue : ( val ) => { max _context = Number ( val ) ; } ,
}
] ;
sliders . forEach ( slider => {
$ ( document ) . on ( "input" , slider . sliderId , function ( ) {
const value = $ ( this ) . val ( ) ;
const formattedValue = slider . format ( value ) ;
slider . setValue ( value ) ;
$ ( slider . counterId ) . text ( formattedValue ) ;
saveSettingsDebounced ( ) ;
} ) ;
} ) ;
//////////////////////////////////////////////////////////////
$ ( "#select_chat_cross" ) . click ( function ( ) {
$ ( "#shadow_select_chat_popup" ) . transition ( {
opacity : 0 ,
duration : 200 ,
easing : animation _easing ,
} ) ;
setTimeout ( function ( ) { $ ( "#shadow_select_chat_popup" ) . css ( "display" , "none" ) ; } , 200 ) ;
//$("#shadow_select_chat_popup").css("display", "none");
$ ( "#load_select_chat_div" ) . css ( "display" , "block" ) ;
} ) ;
if ( navigator . clipboard === undefined ) {
// No clipboard support
$ ( ".mes_copy" ) . remove ( ) ;
}
else {
$ ( document ) . on ( "pointerup" , ".mes_copy" , function ( ) {
if ( this _chid !== undefined || selected _group ) {
const message = $ ( this ) . closest ( ".mes" ) ;
if ( message . data ( "isSystem" ) ) {
return ;
}
try {
var edit _mes _id = $ ( this ) . closest ( ".mes" ) . attr ( "mesid" ) ;
var text = chat [ edit _mes _id ] [ "mes" ] ;
navigator . clipboard . writeText ( text ) ;
toastr . info ( 'Copied!' , '' , { timeOut : 2000 } ) ;
} catch ( err ) {
console . error ( 'Failed to copy: ' , err ) ;
}
}
} ) ;
}
$ ( document ) . on ( "pointerup" , ".mes_prompt" , function ( ) {
let mesIdForItemization = $ ( this ) . closest ( '.mes' ) . attr ( 'mesId' ) ;
console . log ( ` looking for mesID: ${ mesIdForItemization } ` ) ;
if ( itemizedPrompts . length !== undefined && itemizedPrompts . length !== 0 ) {
promptItemize ( itemizedPrompts , mesIdForItemization ) ;
}
} )
2023-09-13 10:52:37 +02:00
$ ( document ) . on ( "pointerup" , "#copyPromptToClipboard" , function ( ) {
let rawPrompt = itemizedPrompts [ PromptArrayItemForRawPromptDisplay ] . rawPrompt ;
let rawPromptValues = rawPrompt ;
if ( Array . isArray ( rawPrompt ) ) {
rawPromptValues = rawPrompt . map ( x => x . content ) . join ( '\n' ) ;
}
navigator . clipboard . writeText ( rawPromptValues ) ;
toastr . info ( 'Copied!' , '' , { timeOut : 2000 } ) ;
} ) ;
2023-07-20 19:32:15 +02:00
$ ( document ) . on ( "pointerup" , "#showRawPrompt" , function ( ) {
//console.log(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt);
console . log ( PromptArrayItemForRawPromptDisplay ) ;
console . log ( itemizedPrompts ) ;
console . log ( itemizedPrompts [ PromptArrayItemForRawPromptDisplay ] . rawPrompt ) ;
let rawPrompt = itemizedPrompts [ PromptArrayItemForRawPromptDisplay ] . rawPrompt ;
let rawPromptValues = rawPrompt ;
if ( Array . isArray ( rawPrompt ) ) {
rawPromptValues = rawPrompt . map ( x => x . content ) . join ( '\n' ) ;
}
//let DisplayStringifiedPrompt = JSON.stringify(itemizedPrompts[PromptArrayItemForRawPromptDisplay].rawPrompt).replace(/\n+/g, '<br>');
$ ( "#rawPromptWrapper" ) . text ( rawPromptValues ) ;
rawPromptPopper . update ( ) ;
$ ( '#rawPromptPopup' ) . toggle ( ) ;
} )
//********************
//***Message Editor***
$ ( document ) . on ( "click" , ".mes_edit" , async function ( ) {
if ( this _chid !== undefined || selected _group ) {
// Previously system messages we're allowed to be edited
/ * c o n s t m e s s a g e = $ ( t h i s ) . c l o s e s t ( " . m e s " ) ;
if ( message . data ( "isSystem" ) ) {
return ;
} * /
let chatScrollPosition = $ ( "#chat" ) . scrollTop ( ) ;
if ( this _edit _mes _id !== undefined ) {
let mes _edited = $ ( ` #chat [mesid=" ${ this _edit _mes _id } "] ` ) . find ( ".mes_edit_done" ) ;
if ( Number ( edit _mes _id ) == count _view _mes - 1 ) { //if the generating swipe (...)
if ( chat [ edit _mes _id ] [ 'swipe_id' ] !== undefined ) {
if ( chat [ edit _mes _id ] [ 'swipes' ] . length === chat [ edit _mes _id ] [ 'swipe_id' ] ) {
run _edit = false ;
}
}
if ( run _edit ) {
hideSwipeButtons ( ) ;
}
}
await messageEditDone ( mes _edited ) ;
}
$ ( this ) . closest ( ".mes_block" ) . find ( ".mes_text" ) . empty ( ) ;
$ ( this ) . closest ( ".mes_block" ) . find ( ".mes_buttons" ) . css ( "display" , "none" ) ;
$ ( this ) . closest ( ".mes_block" ) . find ( ".mes_edit_buttons" ) . css ( "display" , "inline-flex" ) ;
var edit _mes _id = $ ( this ) . closest ( ".mes" ) . attr ( "mesid" ) ;
this _edit _mes _id = edit _mes _id ;
var text = chat [ edit _mes _id ] [ "mes" ] ;
if ( chat [ edit _mes _id ] [ "is_user" ] ) {
this _edit _mes _chname = name1 ;
} else if ( chat [ edit _mes _id ] [ "force_avatar" ] ) {
this _edit _mes _chname = chat [ edit _mes _id ] [ "name" ] ;
} else {
this _edit _mes _chname = name2 ;
}
if ( power _user . trim _spaces ) {
text = text . trim ( ) ;
}
$ ( this )
. closest ( ".mes_block" )
. find ( ".mes_text" )
. append (
` <textarea id='curEditTextarea' class='edit_textarea' style='max-width:auto;'></textarea> `
) ;
2023-07-27 23:38:43 +02:00
$ ( '#curEditTextarea' ) . val ( text ) ;
2023-07-20 19:32:15 +02:00
let edit _textarea = $ ( this )
. closest ( ".mes_block" )
. find ( ".edit_textarea" ) ;
edit _textarea . height ( 0 ) ;
edit _textarea . height ( edit _textarea [ 0 ] . scrollHeight ) ;
edit _textarea . focus ( ) ;
edit _textarea [ 0 ] . setSelectionRange ( //this sets the cursor at the end of the text
edit _textarea . val ( ) . length ,
edit _textarea . val ( ) . length
) ;
if ( this _edit _mes _id == count _view _mes - 1 ) {
$ ( "#chat" ) . scrollTop ( chatScrollPosition ) ;
}
updateEditArrowClasses ( ) ;
}
} ) ;
$ ( document ) . on ( 'input' , '#curEditTextarea' , function ( ) {
if ( power _user . auto _save _msg _edits === true ) {
messageEditAuto ( $ ( this ) ) ;
}
} )
$ ( document ) . on ( "click" , ".extraMesButtonsHint" , function ( e ) {
const elmnt = e . target ;
$ ( elmnt ) . transition ( {
opacity : 0 ,
duration : 150 ,
easing : 'ease-in-out' ,
} ) ;
setTimeout ( function ( ) {
$ ( elmnt ) . hide ( ) ;
$ ( elmnt ) . siblings ( ".extraMesButtons" ) . css ( 'opcacity' , '0' ) ;
$ ( elmnt ) . siblings ( ".extraMesButtons" ) . css ( 'display' , 'flex' ) ;
$ ( elmnt ) . siblings ( ".extraMesButtons" ) . transition ( {
opacity : 1 ,
duration : 150 ,
easing : 'ease-in-out' ,
} ) ;
} , 150 ) ;
} )
$ ( document ) . on ( "click" , ".mes_edit_cancel" , function ( ) {
let text = chat [ this _edit _mes _id ] [ "mes" ] ;
$ ( this ) . closest ( ".mes_block" ) . find ( ".mes_text" ) . empty ( ) ;
$ ( this ) . closest ( ".mes_edit_buttons" ) . css ( "display" , "none" ) ;
$ ( this ) . closest ( ".mes_block" ) . find ( ".mes_buttons" ) . css ( "display" , "" ) ;
$ ( this )
. closest ( ".mes_block" )
. find ( ".mes_text" )
. append ( messageFormatting (
text ,
this _edit _mes _chname ,
chat [ this _edit _mes _id ] . is _system ,
chat [ this _edit _mes _id ] . is _user ,
) ) ;
appendImageToMessage ( chat [ this _edit _mes _id ] , $ ( this ) . closest ( ".mes" ) ) ;
addCopyToCodeBlocks ( $ ( this ) . closest ( ".mes" ) ) ;
this _edit _mes _id = undefined ;
} ) ;
2023-08-23 09:32:48 +02:00
$ ( document ) . on ( "click" , ".mes_edit_up" , async function ( ) {
2023-07-20 19:32:15 +02:00
if ( is _send _press || this _edit _mes _id <= 0 ) {
return ;
}
hideSwipeButtons ( ) ;
const targetId = Number ( this _edit _mes _id ) - 1 ;
const target = $ ( ` #chat .mes[mesid=" ${ targetId } "] ` ) ;
const root = $ ( this ) . closest ( '.mes' ) ;
if ( root . length === 0 || target . length === 0 ) {
return ;
}
root . insertBefore ( target ) ;
target . attr ( "mesid" , this _edit _mes _id ) ;
root . attr ( "mesid" , targetId ) ;
const temp = chat [ targetId ] ;
chat [ targetId ] = chat [ this _edit _mes _id ] ;
chat [ this _edit _mes _id ] = temp ;
this _edit _mes _id = targetId ;
updateViewMessageIds ( ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
showSwipeButtons ( ) ;
} ) ;
2023-08-23 09:32:48 +02:00
$ ( document ) . on ( "click" , ".mes_edit_down" , async function ( ) {
2023-07-20 19:32:15 +02:00
if ( is _send _press || this _edit _mes _id >= chat . length - 1 ) {
return ;
}
hideSwipeButtons ( ) ;
const targetId = Number ( this _edit _mes _id ) + 1 ;
const target = $ ( ` #chat .mes[mesid=" ${ targetId } "] ` ) ;
const root = $ ( this ) . closest ( '.mes' ) ;
if ( root . length === 0 || target . length === 0 ) {
return ;
}
root . insertAfter ( target ) ;
target . attr ( "mesid" , this _edit _mes _id ) ;
root . attr ( "mesid" , targetId ) ;
const temp = chat [ targetId ] ;
chat [ targetId ] = chat [ this _edit _mes _id ] ;
chat [ this _edit _mes _id ] = temp ;
this _edit _mes _id = targetId ;
updateViewMessageIds ( ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
showSwipeButtons ( ) ;
} ) ;
$ ( document ) . on ( "click" , ".mes_edit_copy" , async function ( ) {
const confirmation = await callPopup ( 'Create a copy of this message?' , 'confirm' ) ;
if ( ! confirmation ) {
return ;
}
hideSwipeButtons ( ) ;
let oldScroll = $ ( '#chat' ) [ 0 ] . scrollTop ;
const clone = JSON . parse ( JSON . stringify ( chat [ this _edit _mes _id ] ) ) ; // quick and dirty clone
clone . send _date = Date . now ( ) ;
clone . mes = $ ( this ) . closest ( ".mes" ) . find ( '.edit_textarea' ) . val ( ) ;
if ( power _user . trim _spaces ) {
clone . mes = clone . mes . trim ( ) ;
}
chat . splice ( Number ( this _edit _mes _id ) + 1 , 0 , clone ) ;
addOneMessage ( clone , { insertAfter : this _edit _mes _id } ) ;
updateViewMessageIds ( ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
$ ( '#chat' ) [ 0 ] . scrollTop = oldScroll ;
showSwipeButtons ( ) ;
} ) ;
$ ( document ) . on ( "click" , ".mes_edit_delete" , async function ( event , customData ) {
const fromSlashCommand = customData ? . fromSlashCommand || false ;
2023-08-23 09:32:48 +02:00
const swipeExists = ( ! Array . isArray ( chat [ this _edit _mes _id ] . swipes ) || chat [ this _edit _mes _id ] . swipes . length <= 1 || chat [ this _edit _mes _id ] . is _user || parseInt ( this _edit _mes _id ) !== chat . length - 1 ) ;
2023-07-20 19:32:15 +02:00
if ( power _user . confirm _message _delete && fromSlashCommand !== true ) {
2023-08-19 18:11:45 +02:00
const confirmation = swipeExists ? await callPopup ( "Are you sure you want to delete this message?" , 'confirm' )
: await callPopup ( "<h3>Delete this...</h3> <select id='del_type'><option value='swipe'>Swipe</option><option value='message'>Message</option></select>" , 'confirm' )
2023-07-20 19:32:15 +02:00
if ( ! confirmation ) {
return ;
}
}
const mes = $ ( this ) . closest ( ".mes" ) ;
if ( ! mes ) {
return ;
}
2023-08-20 18:43:58 +02:00
if ( $ ( '#del_type' ) . val ( ) === 'swipe' ) {
2023-08-19 18:11:45 +02:00
const swipe _id = chat [ this _edit _mes _id ] [ 'swipe_id' ] ;
chat [ this _edit _mes _id ] [ 'swipes' ] . splice ( swipe _id , 1 ) ;
if ( swipe _id > 0 ) {
$ ( '.swipe_left:last' ) . click ( ) ;
} else {
$ ( '.swipe_right:last' ) . click ( )
}
} else {
chat . splice ( this _edit _mes _id , 1 ) ;
mes . remove ( ) ;
count _view _mes -- ;
}
2023-07-20 19:32:15 +02:00
this _edit _mes _id = undefined ;
updateViewMessageIds ( ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2023-07-20 19:32:15 +02:00
2023-08-05 17:50:30 +02:00
eventSource . emit ( event _types . MESSAGE _DELETED , count _view _mes ) ;
2023-07-20 19:32:15 +02:00
hideSwipeButtons ( ) ;
showSwipeButtons ( ) ;
} ) ;
$ ( document ) . on ( "click" , ".mes_edit_done" , async function ( ) {
await messageEditDone ( $ ( this ) ) ;
} ) ;
$ ( "#your_name_button" ) . click ( function ( ) {
setUserName ( $ ( '#your_name' ) . val ( ) ) ;
} ) ;
$ ( '#sync_name_button' ) . on ( 'click' , async function ( ) {
const confirmation = await callPopup ( ` <h3>Are you sure?</h3>All user-sent messages in this chat will be attributed to ${ name1 } . ` , 'confirm' ) ;
if ( ! confirmation ) {
return ;
}
for ( const mes of chat ) {
if ( mes . is _user ) {
mes . name = name1 ;
mes . force _avatar = getUserAvatar ( user _avatar ) ;
}
}
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
} ) ;
//Select chat
$ ( "#api_button_novel" ) . on ( 'click' , async function ( e ) {
e . stopPropagation ( ) ;
2023-08-22 13:30:49 +02:00
const api _key _novel = String ( $ ( "#api_key_novel" ) . val ( ) ) . trim ( ) ;
2023-07-20 19:32:15 +02:00
if ( api _key _novel . length ) {
await writeSecret ( SECRET _KEYS . NOVEL , api _key _novel ) ;
}
if ( ! secret _state [ SECRET _KEYS . NOVEL ] ) {
console . log ( 'No secret key saved for NovelAI' ) ;
return ;
}
$ ( "#api_loading_novel" ) . css ( "display" , "inline-block" ) ;
$ ( "#api_button_novel" ) . css ( "display" , "none" ) ;
is _get _status _novel = true ;
is _api _button _press _novel = true ;
// Check near immediately rather than waiting for up to 90s
setTimeout ( getStatusNovel , 10 ) ;
} ) ;
//**************************CHARACTER IMPORT EXPORT*************************//
$ ( "#character_import_button" ) . click ( function ( ) {
$ ( "#character_import_file" ) . click ( ) ;
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( "#character_import_file" ) . on ( "change" , function ( e ) {
$ ( "#rm_info_avatar" ) . html ( "" ) ;
if ( ! e . target . files . length ) {
return ;
}
for ( const file of e . target . files ) {
importCharacter ( file ) ;
}
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( "#export_button" ) . on ( 'click' , function ( e ) {
$ ( '#export_format_popup' ) . toggle ( ) ;
exportPopper . update ( ) ;
} ) ;
2023-08-31 19:31:12 +02:00
2023-07-20 19:32:15 +02:00
$ ( document ) . on ( 'click' , '.export_format' , async function ( ) {
const format = $ ( this ) . data ( 'format' ) ;
if ( ! format ) {
return ;
}
// Save before exporting
await createOrEditCharacter ( ) ;
const body = { format , avatar _url : characters [ this _chid ] . avatar } ;
const response = await fetch ( '/exportcharacter' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( body ) ,
} ) ;
if ( response . ok ) {
const filename = characters [ this _chid ] . avatar . replace ( '.png' , ` . ${ format } ` ) ;
const blob = await response . blob ( ) ;
const a = document . createElement ( "a" ) ;
a . href = URL . createObjectURL ( blob ) ;
a . setAttribute ( "download" , filename ) ;
document . body . appendChild ( a ) ;
a . click ( ) ;
document . body . removeChild ( a ) ;
}
$ ( '#export_format_popup' ) . hide ( ) ;
} ) ;
//**************************CHAT IMPORT EXPORT*************************//
$ ( "#chat_import_button" ) . click ( function ( ) {
$ ( "#chat_import_file" ) . click ( ) ;
} ) ;
$ ( "#chat_import_file" ) . on ( "change" , async function ( e ) {
var file = e . target . files [ 0 ] ;
if ( ! file ) {
return ;
}
var ext = file . name . match ( /\.(\w+)$/ ) ;
if (
! ext ||
( ext [ 1 ] . toLowerCase ( ) != "json" && ext [ 1 ] . toLowerCase ( ) != "jsonl" )
) {
return ;
}
if ( selected _group && file . name . endsWith ( '.json' ) ) {
toastr . warning ( "Only SillyTavern's own format is supported for group chat imports. Sorry!" ) ;
return ;
}
var format = ext [ 1 ] . toLowerCase ( ) ;
$ ( "#chat_import_file_type" ) . val ( format ) ;
var formData = new FormData ( $ ( "#form_import_chat" ) . get ( 0 ) ) ;
formData . append ( 'user_name' , name1 ) ;
$ ( "#select_chat_div" ) . html ( "" ) ;
$ ( "#load_select_chat_div" ) . css ( "display" , "block" ) ;
if ( selected _group ) {
await importGroupChat ( formData ) ;
} else {
await importCharacterChat ( formData ) ;
}
} ) ;
$ ( "#rm_button_group_chats" ) . click ( function ( ) {
selected _button = "group_chats" ;
select _group _chats ( ) ;
} ) ;
$ ( "#rm_button_back_from_group" ) . click ( function ( ) {
selected _button = "characters" ;
select _rm _characters ( ) ;
} ) ;
$ ( "#dupe_button" ) . click ( async function ( ) {
2023-07-22 20:50:21 +02:00
await DupeChar ( ) ;
2023-07-20 19:32:15 +02:00
} ) ;
$ ( document ) . on ( "click" , ".select_chat_block, .bookmark_link, .mes_bookmark" , async function ( ) {
let file _name = $ ( this ) . hasClass ( 'mes_bookmark' )
? $ ( this ) . closest ( '.mes' ) . attr ( 'bookmark_link' )
: $ ( this ) . attr ( "file_name" ) . replace ( ".jsonl" , "" ) ;
if ( ! file _name ) {
return ;
}
if ( selected _group ) {
await openGroupChat ( selected _group , file _name ) ;
} else {
await openCharacterChat ( file _name ) ;
}
$ ( "#shadow_select_chat_popup" ) . css ( "display" , "none" ) ;
$ ( "#load_select_chat_div" ) . css ( "display" , "block" ) ;
} ) ;
$ ( document ) . on ( "click" , ".mes_create_bookmark" , async function ( ) {
var selected _mes _id = $ ( this ) . closest ( ".mes" ) . attr ( "mesid" ) ;
if ( selected _mes _id !== undefined ) {
createNewBookmark ( selected _mes _id ) ;
}
} ) ;
$ ( document ) . on ( "click" , ".mes_stop" , function ( ) {
if ( streamingProcessor ) {
streamingProcessor . abortController . abort ( ) ;
streamingProcessor . isStopped = true ;
streamingProcessor . onStopStreaming ( ) ;
streamingProcessor = null ;
}
if ( abortController ) {
abortController . abort ( ) ;
hideStopButton ( ) ;
}
eventSource . emit ( event _types . GENERATION _STOPPED ) ;
} ) ;
$ ( '.drawer-toggle' ) . on ( 'click' , function ( ) {
var icon = $ ( this ) . find ( '.drawer-icon' ) ;
var drawer = $ ( this ) . parent ( ) . find ( '.drawer-content' ) ;
if ( drawer . hasClass ( 'resizing' ) ) { return }
var drawerWasOpenAlready = $ ( this ) . parent ( ) . find ( '.drawer-content' ) . hasClass ( 'openDrawer' ) ;
let targetDrawerID = $ ( this ) . parent ( ) . find ( '.drawer-content' ) . attr ( 'id' ) ;
const pinnedDrawerClicked = drawer . hasClass ( 'pinnedOpen' ) ;
if ( ! drawerWasOpenAlready ) { //to open the drawer
$ ( '.openDrawer' ) . not ( '.pinnedOpen' ) . addClass ( 'resizing' ) . slideToggle ( 200 , "swing" , async function ( ) {
await delay ( 50 ) ; $ ( this ) . closest ( '.drawer-content' ) . removeClass ( 'resizing' ) ;
} ) ;
$ ( '.openIcon' ) . toggleClass ( 'closedIcon openIcon' ) ;
$ ( '.openDrawer' ) . not ( '.pinnedOpen' ) . toggleClass ( 'closedDrawer openDrawer' ) ;
icon . toggleClass ( 'openIcon closedIcon' ) ;
drawer . toggleClass ( 'openDrawer closedDrawer' ) ;
//console.log(targetDrawerID);
if ( targetDrawerID === 'right-nav-panel' ) {
$ ( this ) . closest ( '.drawer' ) . find ( '.drawer-content' ) . addClass ( 'resizing' ) . slideToggle ( {
duration : 200 ,
easing : "swing" ,
start : function ( ) {
jQuery ( this ) . css ( 'display' , 'flex' ) ; //flex needed to make charlist scroll
} ,
complete : async function ( ) {
await delay ( 50 ) ;
$ ( this ) . closest ( '.drawer-content' ) . removeClass ( 'resizing' ) ;
$ ( "#rm_print_characters_block" ) . trigger ( "scroll" ) ;
}
} )
} else {
$ ( this ) . closest ( '.drawer' ) . find ( '.drawer-content' ) . addClass ( 'resizing' ) . slideToggle ( 200 , "swing" , async function ( ) {
await delay ( 50 ) ; $ ( this ) . closest ( '.drawer-content' ) . removeClass ( 'resizing' ) ;
} ) ;
}
2023-08-25 21:04:06 +02:00
// Set the height of "autoSetHeight" textareas within the drawer to their scroll height
2023-08-27 03:01:13 +02:00
$ ( this ) . closest ( '.drawer' ) . find ( '.drawer-content textarea.autoSetHeight' ) . each ( function ( ) {
2023-08-25 21:04:06 +02:00
resetScrollHeight ( $ ( this ) ) ;
2023-08-27 03:01:13 +02:00
} ) ;
2023-07-20 19:32:15 +02:00
} else if ( drawerWasOpenAlready ) { //to close manually
icon . toggleClass ( 'closedIcon openIcon' ) ;
if ( pinnedDrawerClicked ) {
$ ( drawer ) . addClass ( 'resizing' ) . slideToggle ( 200 , "swing" , async function ( ) {
await delay ( 50 ) ; $ ( this ) . removeClass ( 'resizing' ) ;
} ) ;
}
else {
$ ( '.openDrawer' ) . not ( '.pinnedOpen' ) . addClass ( 'resizing' ) . slideToggle ( 200 , "swing" , async function ( ) {
await delay ( 50 ) ; $ ( this ) . closest ( '.drawer-content' ) . removeClass ( 'resizing' ) ;
} ) ;
}
drawer . toggleClass ( 'closedDrawer openDrawer' ) ;
}
} ) ;
$ ( "html" ) . on ( 'touchstart mousedown' , function ( e ) {
var clickTarget = $ ( e . target ) ;
if ( $ ( '#export_format_popup' ) . is ( ':visible' )
&& clickTarget . closest ( '#export_button' ) . length == 0
&& clickTarget . closest ( '#export_format_popup' ) . length == 0 ) {
$ ( '#export_format_popup' ) . hide ( ) ;
}
const forbiddenTargets = [
'#character_cross' ,
'#avatar-and-name-block' ,
'#shadow_popup' ,
'#world_popup' ,
'.ui-widget' ,
'.text_pole' ,
2023-09-10 01:30:23 +02:00
'#toast-container' ,
2023-09-10 13:39:49 +02:00
'.select2-results' ,
2023-07-20 19:32:15 +02:00
] ;
for ( const id of forbiddenTargets ) {
if ( clickTarget . closest ( id ) . length > 0 ) {
return ;
}
}
var targetParentHasOpenDrawer = clickTarget . parents ( '.openDrawer' ) . length ;
if ( clickTarget . hasClass ( 'drawer-icon' ) == false && ! clickTarget . hasClass ( 'openDrawer' ) ) {
if ( jQuery . find ( '.openDrawer' ) . length !== 0 ) {
if ( targetParentHasOpenDrawer === 0 ) {
//console.log($('.openDrawer').not('.pinnedOpen').length);
$ ( '.openDrawer' ) . not ( '.pinnedOpen' ) . addClass ( 'resizing' ) . slideToggle ( 200 , "swing" , function ( ) {
$ ( this ) . closest ( '.drawer-content' ) . removeClass ( 'resizing' )
} ) ;
$ ( '.openIcon' ) . toggleClass ( 'closedIcon openIcon' ) ;
$ ( '.openDrawer' ) . not ( '.pinnedOpen' ) . toggleClass ( 'closedDrawer openDrawer' ) ;
}
}
}
} ) ;
$ ( document ) . on ( 'click' , '.inline-drawer-toggle' , function ( e ) {
if ( $ ( e . target ) . hasClass ( 'text_pole' ) ) {
return ;
} ;
var icon = $ ( this ) . find ( '.inline-drawer-icon' ) ;
icon . toggleClass ( 'down up' ) ;
icon . toggleClass ( 'fa-circle-chevron-down fa-circle-chevron-up' ) ;
$ ( this ) . closest ( '.inline-drawer' ) . find ( '.inline-drawer-content' ) . stop ( ) . slideToggle ( ) ;
2023-08-25 21:04:06 +02:00
// Set the height of "autoSetHeight" textareas within the inline-drawer to their scroll height
2023-08-27 03:01:13 +02:00
$ ( this ) . closest ( '.inline-drawer' ) . find ( '.inline-drawer-content textarea.autoSetHeight' ) . each ( function ( ) {
2023-08-25 21:04:06 +02:00
resetScrollHeight ( $ ( this ) ) ;
} ) ;
2023-07-20 19:32:15 +02:00
} ) ;
$ ( document ) . on ( 'click' , '.mes .avatar' , function ( ) {
//console.log(isMobile());
//console.log($('body').hasClass('waifuMode'));
/ * i f ( i s M o b i l e ( ) = = = t r u e & & ! $ ( ' b o d y ' ) . h a s C l a s s ( ' w a i f u M o d e ' ) ) {
console . debug ( 'saw mobile regular mode, returning' ) ;
return ;
} else { console . debug ( 'saw valid env for zoomed display' ) } * /
let thumbURL = $ ( this ) . children ( 'img' ) . attr ( 'src' ) ;
let charsPath = '/characters/'
let targetAvatarImg = thumbURL . substring ( thumbURL . lastIndexOf ( "=" ) + 1 ) ;
let charname = targetAvatarImg . replace ( '.png' , '' ) ;
2023-09-05 20:40:22 +02:00
// Remove existing zoomed avatars for characters that are not the clicked character when moving UI is not enabled
if ( ! power _user . movingUI ) {
$ ( '.zoomed_avatar' ) . each ( function ( ) {
const currentForChar = $ ( this ) . attr ( 'forChar' ) ;
if ( currentForChar !== charname && typeof currentForChar !== 'undefined' ) {
console . debug ( ` Removing zoomed avatar for character: ${ currentForChar } ` ) ;
$ ( this ) . remove ( ) ;
}
} ) ;
}
2023-07-20 19:32:15 +02:00
let avatarSrc = isDataURL ( thumbURL ) ? thumbURL : charsPath + targetAvatarImg ;
if ( $ ( ` .zoomed_avatar[forChar=" ${ charname } "] ` ) . length ) {
console . debug ( 'removing container as it already existed' )
$ ( ` .zoomed_avatar[forChar=" ${ charname } "] ` ) . remove ( ) ;
} else {
console . debug ( 'making new container from template' )
const template = $ ( '#zoomed_avatar_template' ) . html ( ) ;
const newElement = $ ( template ) ;
newElement . attr ( 'forChar' , charname ) ;
newElement . attr ( 'id' , ` zoomFor_ ${ charname } ` ) ;
newElement . find ( '.drag-grabber' ) . attr ( 'id' , ` zoomFor_ ${ charname } header ` ) ;
$ ( 'body' ) . append ( newElement ) ;
if ( $ ( this ) . parent ( ) . parent ( ) . attr ( 'is_user' ) == 'true' ) { //handle user avatars
$ ( ` .zoomed_avatar[forChar=" ${ charname } "] img ` ) . attr ( 'src' , thumbURL ) ;
} else if ( $ ( this ) . parent ( ) . parent ( ) . attr ( 'is_system' ) == 'true' ) { //handle system avatars
$ ( ` .zoomed_avatar[forChar=" ${ charname } "] img ` ) . attr ( 'src' , thumbURL ) ;
} else if ( $ ( this ) . parent ( ) . parent ( ) . attr ( 'is_user' ) == 'false' ) { //handle char avatars
$ ( ` .zoomed_avatar[forChar=" ${ charname } "] img ` ) . attr ( 'src' , avatarSrc ) ;
}
loadMovingUIState ( ) ;
$ ( ` .zoomed_avatar[forChar=" ${ charname } "] ` ) . css ( 'display' , 'block' ) ;
dragElement ( newElement )
$ ( ` .zoomed_avatar[forChar=" ${ charname } "] img ` ) . on ( 'dragstart' , ( e ) => {
console . log ( 'saw drag on avatar!' ) ;
e . preventDefault ( ) ;
return false ;
} ) ;
}
} ) ;
$ ( document ) . on ( 'click' , '#OpenAllWIEntries' , function ( ) {
$ ( "#world_popup_entries_list" ) . children ( ) . find ( '.down' ) . click ( )
} ) ;
$ ( document ) . on ( 'click' , '#CloseAllWIEntries' , function ( ) {
$ ( "#world_popup_entries_list" ) . children ( ) . find ( '.up' ) . click ( )
} ) ;
$ ( document ) . on ( 'click' , '.open_alternate_greetings' , openAlternateGreetings ) ;
/* $('#set_character_world').on('click', openCharacterWorldPopup); */
$ ( document ) . keyup ( function ( e ) {
if ( e . key === "Escape" ) {
if ( power _user . auto _save _msg _edits === false ) {
closeMessageEditor ( ) ;
$ ( "#send_textarea" ) . focus ( ) ;
}
if ( power _user . auto _save _msg _edits === true ) {
$ ( ` #chat .mes[mesid=" ${ this _edit _mes _id } "] .mes_edit_done ` ) . click ( )
$ ( "#send_textarea" ) . focus ( ) ;
}
if ( ! this _edit _mes _id && $ ( '#mes_stop' ) . is ( ':visible' ) ) {
$ ( '#mes_stop' ) . trigger ( 'click' ) ;
if ( chat . length && Array . isArray ( chat [ chat . length - 1 ] . swipes ) && chat [ chat . length - 1 ] . swipe _id == chat [ chat . length - 1 ] . swipes . length ) {
$ ( '.last_mes .swipe_left' ) . trigger ( 'click' ) ;
}
}
}
} ) ;
$ ( "#bg-filter" ) . on ( "input" , function ( ) {
2023-08-22 13:30:49 +02:00
const filterValue = String ( $ ( this ) . val ( ) ) . toLowerCase ( ) ;
2023-07-20 19:32:15 +02:00
$ ( "#bg_menu_content > div" ) . each ( function ( ) {
const $bgContent = $ ( this ) ;
if ( $bgContent . attr ( "title" ) . toLowerCase ( ) . includes ( filterValue ) ) {
$bgContent . show ( ) ;
} else {
$bgContent . hide ( ) ;
}
} ) ;
} ) ;
$ ( "#char-management-dropdown" ) . on ( 'change' , async ( e ) => {
let target = $ ( e . target . selectedOptions ) . attr ( 'id' ) ;
switch ( target ) {
case 'set_character_world' :
openCharacterWorldPopup ( ) ;
break ;
case 'set_chat_scenario' :
setScenarioOverride ( ) ;
break ;
case 'renameCharButton' :
renameCharacter ( ) ;
break ;
/ * c a s e ' d u p e _ b u t t o n ' :
DupeChar ( ) ;
break ;
case 'export_button' :
$ ( '#export_format_popup' ) . toggle ( ) ;
exportPopper . update ( ) ;
break ;
* /
case 'import_character_info' :
await importEmbeddedWorldInfo ( ) ;
saveCharacterDebounced ( ) ;
break ;
/ * c a s e ' d e l e t e _ b u t t o n ' :
popup _type = "del_ch" ;
callPopup ( `
< h3 > Delete the character ? < / h 3 >
< b > THIS IS PERMANENT ! < br > < br >
THIS WILL ALSO DELETE ALL < br >
OF THE CHARACTER ' S CHAT FILES . < br > < br > < / b > `
) ;
break ; * /
2023-08-29 00:54:11 +02:00
default :
eventSource . emit ( 'charManagementDropdown' , target ) ;
2023-07-20 19:32:15 +02:00
}
$ ( "#char-management-dropdown" ) . prop ( 'selectedIndex' , 0 ) ;
} ) ;
$ ( document ) . on ( 'click' , '.mes_img_enlarge' , enlargeMessageImage ) ;
$ ( document ) . on ( 'click' , '.mes_img_delete' , deleteMessageImage ) ;
$ ( window ) . on ( 'beforeunload' , ( ) => {
cancelTtsPlay ( ) ;
if ( streamingProcessor ) {
console . log ( 'Page reloaded. Aborting streaming...' ) ;
streamingProcessor . abortController . abort ( ) ;
}
} ) ;
$ ( document ) . on ( 'input' , '.range-block-counter div[contenteditable="true"]' , function ( ) {
const caretPosition = saveCaretPosition ( $ ( this ) . get ( 0 ) ) ;
const myText = $ ( this ) . text ( ) . trim ( ) ;
$ ( this ) . text ( myText ) ; // trim line breaks and spaces
const masterSelector = $ ( this ) . data ( 'for' ) ;
const masterElement = document . getElementById ( masterSelector ) ;
if ( masterElement == null ) {
console . error ( 'Master input element not found for the editable label' , masterSelector ) ;
return ;
}
const myValue = Number ( myText ) ;
if ( Number . isNaN ( myValue ) ) {
console . warn ( 'Label input is not a valid number. Resetting the value' , myText ) ;
$ ( masterElement ) . trigger ( 'input' ) ;
restoreCaretPosition ( $ ( this ) . get ( 0 ) , caretPosition ) ;
return ;
}
const masterMin = Number ( $ ( masterElement ) . attr ( 'min' ) ) ;
const masterMax = Number ( $ ( masterElement ) . attr ( 'max' ) ) ;
if ( myValue < masterMin ) {
console . warn ( 'Label input is less than minimum.' , myText , '<' , masterMin ) ;
restoreCaretPosition ( $ ( this ) . get ( 0 ) , caretPosition ) ;
return ;
}
if ( myValue > masterMax ) {
console . warn ( 'Label input is more than maximum.' , myText , '>' , masterMax ) ;
restoreCaretPosition ( $ ( this ) . get ( 0 ) , caretPosition ) ;
return ;
}
console . debug ( 'Label value OK, setting to the master input control' , myText ) ;
$ ( masterElement ) . val ( myValue ) . trigger ( 'input' ) ;
restoreCaretPosition ( $ ( this ) . get ( 0 ) , caretPosition ) ;
} ) ;
$ ( ".user_stats_button" ) . on ( 'click' , function ( ) {
userStatsHandler ( ) ;
} ) ;
$ ( '#external_import_button' ) . on ( 'click' , async ( ) => {
const html = ` <h3>Enter the URL of the content to import</h3>
Supported sources : < br >
< ul class = "justifyLeft" >
< li > Chub characters ( direct link or id ) < br > Example : < tt > Anonymous / example - character < / t t > < / l i >
< li > Chub lorebooks ( direct link or id ) < br > Example : < tt > lorebooks / bartleby / example - lorebook < / t t > < / l i >
< li > More coming soon ... < / l i >
< ul > `
const input = await callPopup ( html , 'input' ) ;
if ( ! input ) {
console . debug ( 'Custom content import cancelled' ) ;
return ;
}
const url = input . trim ( ) ;
console . debug ( 'Custom content import started' , url ) ;
const request = await fetch ( '/import_custom' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { url } ) ,
} ) ;
if ( ! request . ok ) {
toastr . info ( request . statusText , 'Custom content import failed' ) ;
console . error ( 'Custom content import failed' , request . status , request . statusText ) ;
return ;
}
const data = await request . blob ( ) ;
const customContentType = request . headers . get ( 'X-Custom-Content-Type' ) ;
const fileName = request . headers . get ( 'Content-Disposition' ) . split ( 'filename=' ) [ 1 ] . replace ( /"/g , '' ) ;
const file = new File ( [ data ] , fileName , { type : data . type } ) ;
switch ( customContentType ) {
case 'character' :
processDroppedFiles ( [ file ] ) ;
break ;
case 'lorebook' :
await importWorldInfo ( file ) ;
break ;
default :
2023-08-22 12:07:24 +02:00
toastr . warning ( 'Unknown content type' ) ;
2023-07-20 19:32:15 +02:00
console . error ( 'Unknown content type' , customContentType ) ;
break ;
}
} ) ;
/ * *
* Handles the click event for the third - party extension import button .
* Prompts the user to enter the Git URL of the extension to import .
* After obtaining the Git URL , makes a POST request to '/get_extension' to import the extension .
* If the extension is imported successfully , a success message is displayed .
* If the extension import fails , an error message is displayed and the error is logged to the console .
* After successfully importing the extension , the extension settings are reloaded and a 'EXTENSION_SETTINGS_LOADED' event is emitted .
*
* @ listens # third _party _extension _button # click - The click event of the '#third_party_extension_button' element .
* /
$ ( '#third_party_extension_button' ) . on ( 'click' , async ( ) => {
const html = ` <h3>Enter the Git URL of the extension to import</h3>
< br >
< p > < b > Disclaimer : < / b > P l e a s e b e a w a r e t h a t u s i n g e x t e r n a l e x t e n s i o n s c a n h a v e u n i n t e n d e d s i d e e f f e c t s a n d m a y p o s e s e c u r i t y r i s k s . A l w a y s m a k e s u r e y o u t r u s t t h e s o u r c e b e f o r e i m p o r t i n g a n e x t e n s i o n . W e a r e n o t r e s p o n s i b l e f o r a n y d a m a g e c a u s e d b y t h i r d - p a r t y e x t e n s i o n s . < / p >
< br >
< p > Example : < tt > https : //github.com/author/extension-name </tt></p>`
const input = await callPopup ( html , 'input' ) ;
if ( ! input ) {
console . debug ( 'Extension import cancelled' ) ;
return ;
}
const url = input . trim ( ) ;
console . debug ( 'Extension import started' , url ) ;
const request = await fetch ( '/get_extension' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { url } ) ,
} ) ;
if ( ! request . ok ) {
toastr . info ( request . statusText , 'Extension import failed' ) ;
console . error ( 'Extension import failed' , request . status , request . statusText ) ;
return ;
}
const response = await request . json ( ) ;
toastr . success ( ` Extension " ${ response . display _name } " by ${ response . author } (version ${ response . version } ) has been imported successfully! ` , 'Extension import successful' ) ;
console . debug ( ` Extension " ${ response . display _name } " has been imported successfully at ${ response . extensionPath } ` ) ;
await loadExtensionSettings ( settings ) ;
eventSource . emit ( event _types . EXTENSION _SETTINGS _LOADED ) ;
} ) ;
const $dropzone = $ ( document . body ) ;
$dropzone . on ( 'dragover' , ( event ) => {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
$dropzone . addClass ( 'dragover' ) ;
} ) ;
$dropzone . on ( 'dragleave' , ( event ) => {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
$dropzone . removeClass ( 'dragover' ) ;
} ) ;
$dropzone . on ( 'drop' , async ( event ) => {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
$dropzone . removeClass ( 'dragover' ) ;
const files = Array . from ( event . originalEvent . dataTransfer . files ) ;
2023-08-12 18:47:33 +02:00
if ( ! files . length ) {
await importFromURL ( event . originalEvent . dataTransfer . items , files ) ;
}
2023-07-20 19:32:15 +02:00
processDroppedFiles ( files ) ;
} ) ;
$ ( "#charListGridToggle" ) . on ( 'click' , async ( ) => {
doCharListDisplaySwitch ( ) ;
} ) ;
2023-08-18 15:45:40 +02:00
2023-08-21 16:00:28 +02:00
$ ( "#hideCharPanelAvatarButton" ) . on ( 'click' , ( ) => {
2023-08-18 15:45:40 +02:00
$ ( '#avatar-and-name-block' ) . slideToggle ( )
2023-08-19 14:58:17 +02:00
} ) ;
2023-08-24 22:52:03 +02:00
2023-09-11 00:07:45 +02:00
$ ( document ) . on ( 'mouseup touchend' , '#show_more_messages' , ( ) => {
showMoreMessages ( ) ;
} ) ;
2023-08-24 22:52:03 +02:00
// Added here to prevent execution before script.js is loaded and get rid of quirky timeouts
2023-08-31 19:31:12 +02:00
await firstLoadInit ( ) ;
2023-08-31 16:10:01 +02:00
registerDebugFunction ( 'backfillTokenCounts' , 'Backfill token counters' ,
` Recalculates token counts of all messages in the current chat to refresh the counters.
Useful when you switch between models that have different tokenizers .
This is a visual change only . Your chat will be reloaded . ` , async () => {
for ( const message of chat ) {
// System messages are not counted
if ( message . is _system ) {
continue ;
}
if ( ! message . extra ) {
message . extra = { } ;
}
message . extra . token _count = getTokenCount ( message . mes , 0 ) ;
}
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
} ) ;
2023-07-20 19:32:15 +02:00
} ) ;