2023-07-20 19:32:15 +02:00
import { humanizedDateTime , favsToHotswap , getMessageTimeStamp , dragElement , isMobile , } from "./scripts/RossAscends-mods.js" ;
import { userStatsHandler , statMesProcess } from './scripts/stats.js' ;
2023-08-19 15:29:46 +02:00
import { encode } from "../lib/gpt-2-3-tokenizer/mod.js" ;
import { GPT3BrowserTokenizer } from "../lib/gpt-3-tokenizer/gpt3-tokenizer.js" ;
2023-07-20 19:32:15 +02:00
import {
generateKoboldWithStreaming ,
kai _settings ,
loadKoboldSettings ,
formatKoboldUrl ,
getKoboldGenerationData ,
canUseKoboldStopSequence ,
canUseKoboldStreaming ,
} 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-07-20 19:32:15 +02:00
} from "./scripts/group-chats.js" ;
import {
collapseNewlines ,
loadPowerUserSettings ,
playMessageSound ,
fixMarkdown ,
power _user ,
pygmalion _options ,
tokenizers ,
formatInstructModeChat ,
formatInstructStoryString ,
formatInstructModePrompt ,
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-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 ,
getTokenCountOpenAI ,
chat _completion _sources ,
getTokenizerModel ,
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-10 18:15:52 +02:00
setNovelData ,
2023-08-17 16:40:38 +02:00
adjustNovelInstructionPrompt ,
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 ,
end _trim _to _sentence ,
countOccurrences ,
isOdd ,
sortMoments ,
timestampToMoment ,
download ,
isDataURL ,
getCharaFilename ,
2023-08-08 21:36:42 +02:00
isDigitsOnly ,
2023-07-20 19:32:15 +02:00
} from "./scripts/utils.js" ;
import { extension _settings , getContext , loadExtensionSettings , runGenerationInterceptors , saveMetadataDebounced } from "./scripts/extensions.js" ;
import { executeSlashCommands , getSlashCommandsHelp , registerSlashCommand } from "./scripts/slash-commands.js" ;
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" ;
import { NOTE _MODULE _NAME , metadata _keys , setFloatingPrompt , shouldWIAddPrompt } from "./scripts/authors-note.js" ;
import { deviceInfo } 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-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 ,
setRightTabSelectedClass ,
openCharacterChat ,
saveChat ,
messageFormatting ,
getExtensionPrompt ,
showSwipeButtons ,
hideSwipeButtons ,
changeMainAPI ,
setGenerationProgress ,
updateChatMetadata ,
scrollChatToBottom ,
getTokenCount ,
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 ,
}
// 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' ,
OAI _PRESET _CHANGED : 'oai_preset_changed' ,
WORLDINFO _SETTINGS _UPDATED : 'worldinfo_settings_updated' ,
2023-07-07 22:45:53 +02:00
CHARACTER _EDITED : 'character_edited' ,
2023-07-20 19:32:15 +02:00
}
export const eventSource = new EventEmitter ( ) ;
const gpt3 = new GPT3BrowserTokenizer ( { type : 'gpt3' } ) ;
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!" ,
} ,
] ;
let chat _create _date = 0 ;
2023-07-21 15:42:18 +02:00
let firstRun = false ;
2023-07-20 19:32:15 +02:00
const default _ch _mes = "Hello" ;
let count _view _mes = 0 ;
let generatedPromtCache = "" ;
let generation _started = new Date ( ) ;
let characters = [ ] ;
let this _chid ;
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 getStatusDebounced = debounce ( ( ) => getStatus ( ) , 300_000 ) ;
const saveChatDebounced = debounce ( ( ) => saveChatConditional ( ) , 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 = {
AFTER _SCENARIO : 0 ,
IN _CHAT : 1
} ;
const system _messages = {
help : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes :
` Hello there! Please select the help topic you would like to learn more about:
< ul >
2023-08-07 21:21:10 +02:00
< li > < a href = "#" data - displayHelp = "1" > Slash Commands < / a > ( o r < t t > / h e l p s l a s h < / t t > ) < / l i >
< li > < a href = "#" data - displayHelp = "2" > Formatting < / a > ( o r < t t > / h e l p f o r m a t < / t t > ) < / l i >
< li > < a href = "#" data - displayHelp = "3" > Hotkeys < / a > ( o r < t t > / h e l p h o t k e y s < / t t > ) < / l i >
< li > < a href = "#" data - displayHelp = "4" > { { Macros } } < / a > ( o r < t t > / h e l p m a c r o s < / t t > ) < / l i >
2023-07-20 19:32:15 +02:00
< / u l >
< br > < b > Still got questions left ? The < a target = "_blank" href = "https://docs.sillytavern.app/" > Official SillyTavern Documentation Website < / a > h a s m u c h m o r e i n f o r m a t i o n ! < / b > `
} ,
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 :
` Hotkeys/Keybinds:
< ul >
< li > < tt > Up < / t t > = E d i t l a s t m e s s a g e i n c h a t < / l i >
< li > < tt > Ctrl + Up < / t t > = E d i t l a s t U S E R m e s s a g e i n c h a t < / l i >
< li > < tt > Left < / t t > = s w i p e l e f t < / l i >
< li > < tt > Right < / t t > = s w i p e r i g h t ( N O T E : s w i p e h o t k e y s a r e d i s a b l e d w h e n c h a t b a r h a s s o m e t h i n g t y p e d i n t o i t ) < / l i >
< li > < tt > Ctrl + Left < / t t > = v i e w l o c a l l y s t o r e d v a r i a b l e s ( i n t h e b r o w s e r c o n s o l e w i n d o w ) < / l i >
< li > < tt > Enter < / t t > ( w i t h c h a t b a r s e l e c t e d ) = s e n d y o u r m e s s a g e t o A I < / l i >
< li > < tt > Ctrl + Enter < / t t > = R e g e n e r a t e t h e l a s t A I r e s p o n s e < / l i >
< li > < tt > Escape < / t t > = s t o p A I r e s p o n s e g e n e r a t i o n < / l i >
< li > < tt > Ctrl + Shift + Up < / t t > = S c r o l l t o c o n t e x t l i n e < / l i >
< li > < tt > Ctrl + Shift + Down < / t t > = S c r o l l c h a t t o b o t t o m < / l i >
< / u l > `
} ,
formatting : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes :
` Text formatting commands:
< ul >
< li > < tt > * text * < /tt> - displays as <i>italics</i > < / l i >
< li > < tt > * * text * * < / t t > - d i s p l a y s a s < b > b o l d < / b > < / l i >
< li > < tt > * * * text * * * < /tt> - displays as <b><i>bold italics</i > < / b > < / l i >
2023-08-08 11:03:39 +02:00
< li > < tt > ` + " ` ` ` " + `text` + " ` ` ` " + ` < / t t > - d i s p l a y s a s a c o d e b l o c k ( n e w l i n e s a l l o w e d b e t w e e n t h e b a c k t i c k s ) < / l i >
< pre >
< code >
like
this
< / c o d e >
< / p r e >
< li > < tt > ` + " ` " + `text` + " ` " + ` < / t t > - d i s p l a y s a s < c o d e > i n l i n e c o d e < / c o d e > < / l i >
2023-08-08 10:42:28 +02:00
< li > < tt > ` + "> " + ` text ` + ` < / t t > - d i s p l a y s a s a b l o c k q u o t e ( n o t e t h e s p a c e a f t e r > ) < / l i >
< blockquote > like this < / b l o c k q u o t e >
< li > < tt > ` + "# " + ` text ` + ` < / t t > - d i s p l a y s a s a l a r g e h e a d e r ( n o t e t h e s p a c e ) < / l i >
< h1 > like this < / h 1 >
< li > < tt > ` + "## " + ` text ` + ` < / t t > - d i s p l a y s a s a m e d i u m h e a d e r ( n o t e t h e s p a c e ) < / l i >
< h2 > like this < / h 2 >
< li > < tt > ` + "### " + ` text ` + ` < / t t > - d i s p l a y s a s a s m a l l h e a d e r ( n o t e t h e s p a c e ) < / l i >
< h3 > like this < / h 3 >
2023-07-20 19:32:15 +02:00
< li > < tt > $$ text $$ < / t t > - r e n d e r s a L a T e X f o r m u l a ( i f e n a b l e d ) < / l i >
< li > < tt > $ text $ < / t t > - r e n d e r s a n A s c i i M a t h f o r m u l a ( i f e n a b l e d ) < / l i >
< / u l > `
} ,
macros : {
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes :
` System-wide Replacement Macros:
< ul >
< li > < tt > { { user } } < / t t > - y o u r c u r r e n t P e r s o n a u s e r n a m e < / l i >
< li > < tt > { { char } } < / t t > - t h e C h a r a c t e r ' s n a m e < / l i >
< li > < tt > { { input } } < / t t > - t h e u s e r i n p u t < / l i >
< li > < tt > { { time } } < / t t > - t h e c u r r e n t t i m e < / l i >
< li > < tt > { { date } } < / t t > - t h e c u r r e n t d a t e < / l i >
< li > < tt > { { idle _duration } } < / t t > - t h e t i m e s i n c e t h e l a s t u s e r m e s s a g e w a s s e n t < / l i >
< li > < tt > { { random : ( args ) } } < / t t > - r e t u r n s a r a n d o m i t e m f r o m t h e l i s t . ( e x : { { r a n d o m : 1 , 2 , 3 , 4 } } w i l l r e t u r n 1 o f t h e 4 n u m b e r s a t r a n d o m . W o r k s w i t h t e x t l i s t s t o o . < / l i >
< li > < tt > { { roll : ( formula ) } } < / t t > - r o l l s a d i c e . ( e x : { { r o l l : 1 d 6 } } w i l l r o l l a 6 - s i d e d d i c e a n d r e t u r n a n u m b e r b e t w e e n 1 a n d 6 ) < / l i >
< / u l > `
} ,
welcome :
{
name : systemUserName ,
force _avatar : system _avatar ,
is _user : false ,
is _system : true ,
is _name : true ,
mes : [
'<h3><span id="version_display_welcome">SillyTavern</span><div id="version_display_welcome"></div></h3>' ,
"<a href='https://docs.sillytavern.app/usage/update/' target='_blank'>Want to update?</a>" ,
'<hr>' ,
'<h3>How to start chatting?</h3>' ,
'<ol>' ,
'<li>Click <code><i class="fa-solid fa-plug"></i></code> and select a <a href="https://docs.sillytavern.app/usage/api-connections/" target_"blank">Chat API</a>.</li>' ,
'<li>Click <code><i class="fa-solid fa-address-card"></i></code> and pick a character</li>' ,
'</ol>' ,
'<hr>' ,
'<h3>Want more characters?</h3>' ,
'<i>Not controlled by SillyTavern team.</i>' ,
'<ul>' ,
'<li><a target="_blank" href="https://discord.gg/pygmalionai">Pygmalion AI Discord</a></li>' ,
'<li><a target="_blank" href="https://chub.ai/">Chub (NSFW)</a></li>' ,
'</ul>' ,
'<hr>' ,
'<h3>Confused or lost?</h3>' ,
'<ul>' ,
'<li><span class="note-link-span">?</span> - click these icons!</li>' ,
'<li>Enter <code>/?</code> in the chat bar</li>' ,
'<li><a target="_blank" href="https://docs.sillytavern.app/">SillyTavern Documentation Site</a></li>' ,
'<li><a target="_blank" href="https://docs.sillytavern.app/extras/installation/">Extras Installation Guide</a></li>' ,
'</ul>' ,
'<hr>' ,
'<h3>Still have questions?</h3 > ' ,
'<ul>' ,
'<li><a target="_blank" href="https://discord.gg/RZdyAEUPvj">Join the SillyTavern Discord</a></li>' ,
'<li><a target="_blank" href="https://github.com/SillyTavern/SillyTavern/issues">Post a GitHub issue</a></li>' ,
'<li><a target="_blank" href="https://github.com/SillyTavern/SillyTavern#questions-or-suggestions">Contact the developers</a></li>' ,
] . join ( '' )
} ,
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-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
}
} ) ;
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 ) {
displayVersion += ` ' ${ data . gitBranch } '( ${ data . gitRevision } ) ` ;
}
$ ( '#version_display' ) . text ( displayVersion ) ;
$ ( '#version_display_welcome' ) . text ( displayVersion ) ;
} catch ( err ) {
console . error ( "Couldn't get client version" , err ) ;
}
}
2023-08-04 13:17:05 +02:00
function getTokenizerBestMatch ( ) {
if ( main _api === 'novel' ) {
if ( nai _settings . model _novel . includes ( 'krake' ) || nai _settings . model _novel . includes ( 'euterpe' ) ) {
return tokenizers . CLASSIC ;
}
if ( nai _settings . model _novel . includes ( 'clio' ) ) {
return tokenizers . NERD ;
}
if ( nai _settings . model _novel . includes ( 'kayra' ) ) {
return tokenizers . NERD2 ;
}
}
if ( main _api === 'kobold' || main _api === 'textgenerationwebui' || main _api === 'koboldhorde' ) {
return tokenizers . LLAMA ;
}
return power _user . NONE ;
}
2023-07-20 19:32:15 +02:00
function getTokenCount ( str , padding = undefined ) {
2023-08-06 06:58:26 +02:00
if ( typeof str !== 'string' || ! str ? . length ) {
2023-07-20 19:32:15 +02:00
return 0 ;
}
let tokenizerType = power _user . tokenizer ;
if ( main _api === 'openai' ) {
if ( padding === power _user . token _padding ) {
// For main "shadow" prompt building
tokenizerType = tokenizers . NONE ;
} else {
// For extensions and WI
return getTokenCountOpenAI ( str ) ;
}
}
2023-08-04 13:17:05 +02:00
if ( tokenizerType === tokenizers . BEST _MATCH ) {
tokenizerType = getTokenizerBestMatch ( ) ;
}
2023-07-20 19:32:15 +02:00
if ( padding === undefined ) {
padding = 0 ;
}
switch ( tokenizerType ) {
case tokenizers . NONE :
return Math . ceil ( str . length / CHARACTERS _PER _TOKEN _RATIO ) + padding ;
case tokenizers . GPT3 :
return gpt3 . encode ( str ) . bpe . length + padding ;
case tokenizers . CLASSIC :
return encode ( str ) . length + padding ;
case tokenizers . LLAMA :
return countTokensRemote ( '/tokenize_llama' , str , padding ) ;
case tokenizers . NERD :
return countTokensRemote ( '/tokenize_nerdstash' , str , padding ) ;
case tokenizers . NERD2 :
return countTokensRemote ( '/tokenize_nerdstash_v2' , str , padding ) ;
case tokenizers . API :
return countTokensRemote ( '/tokenize_via_api' , str , padding ) ;
default :
console . warn ( "Unknown tokenizer type" , tokenizerType ) ;
return Math . ceil ( str . length / CHARACTERS _PER _TOKEN _RATIO ) + padding ;
}
}
function countTokensRemote ( endpoint , str , padding ) {
let tokenCount = 0 ;
jQuery . ajax ( {
async : false ,
type : 'POST' ,
url : endpoint ,
data : JSON . stringify ( { text : str } ) ,
dataType : "json" ,
contentType : "application/json" ,
success : function ( data ) {
tokenCount = data . count ;
}
} ) ;
return tokenCount + padding ;
}
2023-08-02 21:35:05 +02:00
function getTextTokensRemote ( endpoint , str ) {
let ids = [ ] ;
jQuery . ajax ( {
async : false ,
type : 'POST' ,
url : endpoint ,
data : JSON . stringify ( { text : str } ) ,
dataType : "json" ,
contentType : "application/json" ,
success : function ( data ) {
ids = data . ids ;
}
} ) ;
return ids ;
}
export function getTextTokens ( tokenizerType , str ) {
switch ( tokenizerType ) {
case tokenizers . LLAMA :
return getTextTokensRemote ( '/tokenize_llama' , str ) ;
case tokenizers . NERD :
return getTextTokensRemote ( '/tokenize_nerdstash' , str ) ;
case tokenizers . NERD2 :
return getTextTokensRemote ( '/tokenize_nerdstash_v2' , str ) ;
default :
console . warn ( "Calling getTextTokens with unsupported tokenizer type" , tokenizerType ) ;
return [ ] ;
}
}
2023-07-20 19:32:15 +02:00
function reloadMarkdownProcessor ( render _formulas = false ) {
if ( render _formulas ) {
converter = new showdown . Converter ( {
emoji : "true" ,
underline : "true" ,
2023-08-10 12:18:12 +02:00
tables : "true" ,
2023-08-01 15:24:54 +02:00
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 ( {
emoji : "true" ,
literalMidWordUnderscores : "true" ,
2023-08-01 15:24:54 +02:00
parseImgDimensions : "true" ,
2023-08-10 12:18:12 +02:00
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 ;
}
}
export const CHARACTERS _PER _TOKEN _RATIO = 3.35 ;
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);
let interval _timer _novel = setInterval ( getStatusNovel , 90000 ) ;
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 add _mes _without _animation = false ;
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" ;
var 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 is _pygmalion = false ;
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 ) ;
} ) ;
///// initialization protocol ////////
$ . get ( "/csrf-token" ) . then ( async ( data ) => {
token = data . token ;
sendSystemMessage ( system _message _types . WELCOME ) ;
await readSecretState ( ) ;
await getClientVersion ( ) ;
await getSettings ( "def" ) ;
await getUserAvatars ( ) ;
await getCharacters ( ) ;
await getBackgrounds ( ) ;
} ) ;
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 ( ) ;
if ( online _status !== "no_connection" ) {
getStatusDebounced ( ) ;
}
}
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" ;
}
if ( ( online _status . toLowerCase ( ) . indexOf ( "pygmalion" ) != - 1 && power _user . pygmalion _formatting == pygmalion _options . AUTO )
|| ( online _status !== "no_connection" && power _user . pygmalion _formatting == pygmalion _options . ENABLED ) ) {
is _pygmalion = true ;
online _status += " (Pyg. formatting on)" ;
} else {
is _pygmalion = false ;
}
// determine if we can use stop sequence and streaming
if ( main _api === "kobold" || main _api === "koboldhorde" ) {
kai _settings . use _stop _sequence = canUseKoboldStopSequence ( data . version ) ;
kai _settings . can _use _streaming = canUseKoboldStreaming ( data . koboldVersion ) ;
}
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 ( ) ;
if ( online _status !== "no_connection" ) {
getStatusDebounced ( ) ;
}
} ,
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-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 ) ;
template . find ( '.ch_name' ) . text ( item . name ) ;
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 ) {
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 ,
position : 'top' ,
showPageNumbers : false ,
showSizeChanger : true ,
prevText : '<' ,
nextText : '>' ,
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-07-20 19:32:15 +02:00
} ) ;
favsToHotswap ( ) ;
2023-08-18 22:13:15 +02:00
if ( fullRefresh ) {
printTagFilters ( tag _filter _types . character ) ;
printTagFilters ( tag _filter _types . group _member ) ;
await delay ( 300 ) ;
displayOverrideWarnings ( ) ;
}
}
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
if ( chatfile . replace ( '.jsonl' , '' ) === characters [ this _chid ] . chat ) {
chat _metadata = { } ;
await replaceCurrentChat ( ) ;
}
}
}
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 ( ) ;
}
}
}
function printMessages ( ) {
chat . forEach ( function ( item , i , arr ) {
addOneMessage ( item , { scroll : i === arr . 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 ( ) ;
printMessages ( ) ;
}
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 = '' ;
}
// 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 ) {
mes = fixMarkdown ( mes ) ;
}
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
}
//function to hide any <tags> from AI response output
/ * i f ( p o w e r _ u s e r . r e m o v e X M L & & c h _ n a m e & & ! i s U s e r & & ! i s S y s t e m ) {
//console.log('incoming mes')
//console.log(mes)
mes = mes . replaceAll ( /</g , "<" ) ;
mes = mes . replaceAll ( />/g , ">" ) ;
mes = mes . replaceAll ( /<<[^>>]+>>/g , "" ) ;
//console.log('mes after removed <tags>')
//console.log(mes)
} * /
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-14 04:57:49 +02:00
* @ param { jQuery } 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-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 ,
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 } ` ) ;
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 } ) ;
} ) ;
}
}
}
function addOneMessage ( mes , { type = "normal" , insertAfter = null , scroll = true } = { } ) {
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 ;
generatedPromtCache = "" ;
//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 ? ? "" ) ;
const bookmarkLink = mes ? . extra ? . bookmark _link ? ? '' ;
let params = {
mesId : count _view _mes ,
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 ,
... formatGenerationTimer ( mes . gen _started , mes . gen _finished ) ,
} ;
const HTMLForEachMes = getMessageFromTemplate ( params ) ;
if ( type !== 'swipe' ) {
if ( ! insertAfter ) {
$ ( "#chat" ) . append ( HTMLForEachMes ) ;
}
else {
const target = $ ( "#chat" ) . find ( ` .mes[mesid=" ${ insertAfter } "] ` ) ;
$ ( HTMLForEachMes ) . insertAfter ( target ) ;
$ ( HTMLForEachMes ) . find ( '.swipe_left' ) . css ( 'display' , 'none' ) ;
$ ( HTMLForEachMes ) . find ( '.swipe_right' ) . css ( 'display' , 'none' ) ;
}
}
const newMessageId = type == 'swipe' ? count _view _mes - 1 : count _view _mes ;
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' ) {
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . find ( '.mes_text' ) . html ( '' ) ;
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . find ( '.mes_text' ) . append ( messageText ) ;
appendImageToMessage ( mes , $ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) ) ;
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . attr ( 'title' , title ) ;
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . 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 ) {
insertSVGIcon ( $ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) , params . extra ) ;
}
2023-07-20 19:32:15 +02:00
if ( mes . swipe _id == mes . swipes . length - 1 ) {
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . find ( '.mes_timer' ) . text ( params . timerValue ) ;
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . find ( '.mes_timer' ) . attr ( 'title' , params . timerTitle ) ;
} else {
$ ( "#chat" ) . find ( ` [mesid=" ${ count _view _mes - 1 } "] ` ) . find ( '.mes_timer' ) . html ( '' ) ;
}
} 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
if ( ! insertAfter && scroll ) {
$ ( '#chat .mes' ) . last ( ) . addClass ( 'last_mes' ) ;
$ ( '#chat .mes' ) . eq ( - 2 ) . removeClass ( 'last_mes' ) ;
hideSwipeButtons ( ) ;
showSwipeButtons ( ) ;
scrollChatToBottom ( ) ;
}
}
function getUserAvatar ( avatarImg ) {
return ` User Avatars/ ${ avatarImg } ` ;
}
function formatGenerationTimer ( gen _started , gen _finished ) {
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 ` ,
] . 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-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 ) ;
} ) ;
}
function getStoppingStrings ( isImpersonate , addSpace ) {
const charString = ` \n ${ name2 } : ` ;
const youString = ` \n You: ` ;
const userString = ` \n ${ name1 } : ` ;
const result = isImpersonate ? [ charString ] : [ youString ] ;
result . push ( userString ) ;
2023-07-26 20:08:25 +02:00
if ( ! is _pygmalion && result . includes ( youString ) ) {
result . splice ( result . indexOf ( youString ) , 1 ) ;
}
2023-07-20 19:32:15 +02:00
// 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-17 10:47:15 +02:00
function addInstructSequence ( sequence ) {
// Cohee: oobabooga's textgen always appends newline before the sequence as a stopping string
// But it's a problem for Metharme which doesn't use newlines to separate them.
const wrap = ( s ) => power _user . instruct . wrap ? '\n' + s : s ;
// Sequence must be a non-empty string
if ( typeof sequence === 'string' && sequence . length > 0 ) {
// If sequence is just a whitespace or newline - we don't want to make it a stopping string
// User can always add it as a custom stop string if really needed
if ( sequence . trim ( ) . length > 0 ) {
const wrappedSequence = wrap ( sequence ) ;
// Need to respect "insert macro" setting
const stopString = power _user . instruct . macro ? substituteParams ( wrappedSequence ) : wrappedSequence ;
result . push ( stopString ) ;
}
}
}
2023-07-20 19:32:15 +02:00
if ( power _user . instruct . enabled ) {
2023-08-18 23:02:59 +02:00
const input _sequence = power _user . instruct . input _sequence ;
const output _sequence = power _user . instruct . output _sequence ;
const last _output _sequence = power _user . instruct . last _output _sequence ;
const combined _sequence = ` ${ input _sequence } \n ${ output _sequence } \n ${ last _output _sequence } ` ;
combined _sequence . split ( '\n' ) . filter ( ( line , index , self ) => self . indexOf ( line ) === index ) . forEach ( addInstructSequence ) ;
2023-07-20 19:32:15 +02:00
}
if ( power _user . custom _stopping _strings ) {
const customStoppingStrings = getCustomStoppingStrings ( ) ;
2023-08-04 16:53:49 +02:00
if ( power _user . custom _stopping _strings _macro ) {
result . push ( ... customStoppingStrings . map ( x => substituteParams ( x , name1 , name2 ) ) ) ;
} else {
result . push ( ... customStoppingStrings ) ;
}
2023-07-20 19:32:15 +02:00
}
return addSpace ? result . map ( x => ` ${ x } ` ) : result ;
}
// 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 ;
}
const newMessage = { ... systemMessage , send _date : humanizedDateTime ( ) } ;
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 ;
}
const forbiddenMatches = [ 'user' , 'char' , 'time' , 'date' , 'random' , 'idle_duration' , 'roll' ] ;
const found = [ ] ;
const rxp = /\{\{([\s\S]+?)\}\}/gm ;
//const rxp = /{([^}]+)}/g;
let curMatch ;
while ( ( curMatch = rxp . exec ( message ) ) ) {
const match = curMatch [ 1 ] . trim ( ) ;
// Ignore random/roll pattern matches
2023-08-08 21:36:42 +02:00
if ( /^random[ : ].+/i . test ( match ) || /^roll[ : ].+/i . test ( match ) ) {
2023-07-20 19:32:15 +02:00
continue ;
}
if ( forbiddenMatches . includes ( match . toLowerCase ( ) ) ) {
continue ;
}
found . push ( match ) ;
}
let biasString = '' ;
if ( found . length ) {
biasString = ` ${ found . join ( " " ) } `
}
return biasString ;
}
function cleanGroupMessage ( getMessage ) {
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 ;
}
const indexOfMember = getMessage . indexOf ( ` ${ name } : ` ) ;
if ( indexOfMember != - 1 ) {
getMessage = getMessage . substr ( 0 , indexOfMember ) ;
}
}
}
return getMessage ;
}
function getPersonaDescription ( storyString ) {
if ( ! power _user . persona _description ) {
return storyString ;
}
switch ( power _user . persona _description _position ) {
case persona _description _positions . BEFORE _CHAR :
return ` ${ substituteParams ( power _user . persona _description ) } \n ${ storyString } ` ;
case persona _description _positions . AFTER _CHAR :
2023-08-01 11:46:01 +02:00
return ` ${ storyString } ${ substituteParams ( power _user . persona _description ) } \n ` ;
2023-07-20 19:32:15 +02:00
default :
if ( 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 ] ) ;
}
return storyString ;
}
}
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 ) {
if ( is _pygmalion ) {
value = value . replace ( /{{user}}:/gi , 'You:' ) ;
value = value . replace ( /<USER>:/gi , 'You:' ) ;
}
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-07-20 19:32:15 +02:00
|| ( main _api == 'kobold' && kai _settings . streaming _kobold && kai _settings . can _use _streaming )
|| ( 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' } ) ;
}
onStartStreaming ( text ) {
let messageId = - 1 ;
if ( this . type == "impersonate" ) {
$ ( '#send_textarea' ) . val ( '' ) . trigger ( 'input' ) ;
}
else {
saveReply ( this . type , text ) ;
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 ( ) ;
const timePassed = formatGenerationTimer ( this . timeStarted , currentTime ) ;
chat [ messageId ] [ 'is_name' ] = isName ;
chat [ messageId ] [ 'mes' ] = processedText ;
chat [ messageId ] [ 'gen_started' ] = this . timeStarted ;
chat [ messageId ] [ 'gen_finished' ] = currentTime ;
if ( this . type == 'swipe' && Array . isArray ( chat [ messageId ] [ 'swipes' ] ) ) {
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 ( ) ;
}
}
onFinishStreaming ( messageId , text ) {
this . hideMessageButtons ( this . messageId ) ;
this . onProgressStreaming ( messageId , text , true ) ;
addCopyToCodeBlocks ( $ ( ` #chat .mes[mesid=" ${ messageId } "] ` ) ) ;
saveChatConditional ( ) ;
activateSendButtons ( ) ;
showSwipeButtons ( ) ;
setGenerationProgress ( 0 ) ;
generatedPromtCache = '' ;
//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 ( ) ;
const eventType = this . type !== 'impersonate' ? event _types . MESSAGE _RECEIVED : event _types . IMPERSONATE _READY ;
const eventData = this . type !== 'impersonate' ? this . messageId : text ;
eventSource . emit ( eventType , eventData ) ;
}
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 ( ) ;
}
nullStreamingGeneration ( ) {
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 ) {
this . messageId = this . onStartStreaming ( this . firstMessageText ) ;
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
const magName = isImpersonate ? ( is _pygmalion ? 'You' : name1 ) : name2 ;
if ( isInstruct ) {
message _already _generated = formatInstructModePrompt ( magName , isImpersonate , false , name1 , name2 ) ;
} 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 ;
}
if ( main _api == 'kobold' && kai _settings . streaming _kobold && ! kai _settings . can _use _streaming ) {
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 ;
}
// Hide swipes on either multigen or real streaming
2023-08-14 23:01:55 +02:00
if ( ( isStreamingEnabled ( ) || isMultigenEnabled ( ) ) && ! 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' ) ;
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-13 17:43:17 +02:00
else if ( type !== 'quiet' && type !== "swipe" && ! isImpersonate && ! dryRun ) {
2023-07-20 19:32:15 +02:00
chat . length = chat . length - 1 ;
count _view _mes -= 1 ;
$ ( '#chat' ) . children ( ) . last ( ) . hide ( 500 , function ( ) {
$ ( this ) . remove ( ) ;
} ) ;
await eventSource . emit ( event _types . MESSAGE _DELETED , chat . length ) ;
}
}
if ( ! type && ! textareaText && power _user . continue _on _send && ! selected _group && chat . length && ! chat [ chat . length - 1 ] [ 'is_user' ] ) {
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 ) ;
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
// Parse example messages
if ( ! mesExamples . startsWith ( '<START>' ) ) {
mesExamples = '<START>\n' + mesExamples . trim ( ) ;
}
if ( mesExamples . replace ( /<START>/gi , '' ) . trim ( ) . length === 0 ) {
mesExamples = '' ;
}
2023-08-17 23:41:21 +02:00
2023-08-18 11:32:52 +02:00
const exampleSeparator = power _user . context . example _separator ? ` ${ power _user . context . example _separator } \n ` : '' ;
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
// 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 } ` ) ;
2023-08-17 21:47:34 +02:00
const storyStringParams = {
description : charDescription ,
personality : charPersonality ,
scenario : Scenario ,
char : name2 ,
user : name1 ,
} ;
2023-07-20 19:32:15 +02:00
2023-08-17 21:47:34 +02:00
let storyString = renderStoryString ( storyStringParams ) ;
2023-07-20 19:32:15 +02:00
// kingbri MARK: - Make sure the prompt bias isn't the same as the user bias
if ( ( promptBias && ! isUserPromptBias ) || power _user . always _force _name2 || is _pygmalion ) {
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 ;
}
chat2 [ i ] = formatMessageHistoryItem ( coreChat [ j ] , isInstruct ) ;
// Do not suffix the message for continuation
if ( i === 0 && isContinue ) {
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
storyString = getPersonaDescription ( storyString ) ;
// Call combined AN into Generate
let allAnchors = getAllExtensionPrompts ( ) ;
const afterScenarioAnchor = getExtensionPrompt ( extension _prompt _types . AFTER _SCENARIO ) ;
let zeroDepthAnchor = getExtensionPrompt ( extension _prompt _types . IN _CHAT , 0 , ' ' ) ;
// Pre-format the World Info into the story string
if ( main _api !== 'openai' ) {
storyString = worldInfoBefore + storyString + worldInfoAfter ;
}
// Format the instruction string
if ( isInstruct ) {
storyString = formatInstructStoryString ( storyString , systemPrompt ) ;
}
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 = '' ;
function getMessagesTokenCount ( ) {
const encodeString = [
storyString ,
examplesString ,
chatString ,
allAnchors ,
quiet _prompt ,
] . 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 ( '' ) ;
}
let cyclePrompt = '' ;
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 ) ;
async function runGenerate ( cycleGenerationPromt = '' ) {
2023-08-14 23:01:55 +02:00
if ( ! dryRun ) {
is _send _press = true ;
}
2023-07-20 19:32:15 +02:00
generatedPromtCache += cycleGenerationPromt ;
if ( generatedPromtCache . length == 0 || type === 'continue' ) {
if ( main _api === 'openai' ) {
generateOpenAIPromptCache ( ) ;
}
console . debug ( 'generating prompt' ) ;
chatString = "" ;
arrMes = arrMes . reverse ( ) ;
arrMes . forEach ( function ( item , i , arr ) { //For added anchors and others
// OAI doesn't need all of this
if ( main _api === 'openai' ) {
return ;
}
if ( i === arrMes . length - 1 && ! item . trim ( ) . startsWith ( name1 + ":" ) ) {
2023-08-02 20:00:01 +02:00
//if (textareaText == "") {
2023-08-03 06:21:38 +02:00
// Cohee: I think this was added to allow the model to continue
// where it left off by removing the trailing newline at the end
// that was added by chat2 generator. This causes problems with
// instruct mode that could not have a trailing newline. So we're
// removing a newline ONLY at the end of the string if it exists.
item = item . replace ( /\n?$/ , '' ) ;
//item = item.substr(0, item.length - 1);
2023-08-02 20:00:01 +02:00
//}
2023-07-20 19:32:15 +02:00
}
if ( is _pygmalion && ! isInstruct ) {
if ( item . trim ( ) . startsWith ( name1 ) ) {
item = item . replace ( name1 + ':' , 'You:' ) ;
}
}
if ( i === 0 ) {
// Process those that couldn't get that far
for ( let upperDepth = 100 ; upperDepth >= arrMes . length ; upperDepth -- ) {
const upperAnchor = getExtensionPrompt ( extension _prompt _types . IN _CHAT , upperDepth ) ;
if ( upperAnchor && upperAnchor . length ) {
item = upperAnchor + item ;
}
}
}
const anchorDepth = Math . abs ( i - arrMes . length + 1 ) ;
const extensionAnchor = getExtensionPrompt ( extension _prompt _types . IN _CHAT , anchorDepth ) ;
if ( anchorDepth > 0 && extensionAnchor && extensionAnchor . length ) {
item += extensionAnchor ;
}
mesSend [ mesSend . length ] = item ;
} ) ;
}
let mesExmString = '' ;
let mesSendString = '' ;
function setPromtString ( ) {
if ( main _api == 'openai' ) {
return ;
}
console . debug ( '--setting Prompt string' ) ;
mesExmString = pinExmString ? ? mesExamplesArray . slice ( 0 , count _exm _add ) . join ( '' ) ;
mesSendString = '' ;
for ( let j = 0 ; j < mesSend . length ; j ++ ) {
const isBottom = j === mesSend . length - 1 ;
mesSendString += mesSend [ j ] ;
if ( isBottom ) {
mesSendString = modifyLastPromptLine ( mesSendString ) ;
}
}
}
function modifyLastPromptLine ( mesSendString ) {
// Add quiet generation prompt at depth 0
if ( quiet _prompt && quiet _prompt . length ) {
const name = is _pygmalion ? 'You' : name1 ;
const quietAppend = isInstruct ? formatInstructModeChat ( name , quiet _prompt , false , true , false , name1 , name2 ) : ` \n ${ name } : ${ quiet _prompt } ` ;
mesSendString += quietAppend ;
// Bail out early
return mesSendString ;
}
// Get instruct mode line
if ( isInstruct && tokens _already _generated === 0 ) {
const name = isImpersonate ? ( is _pygmalion ? 'You' : name1 ) : name2 ;
mesSendString += formatInstructModePrompt ( name , isImpersonate , promptBias , name1 , name2 ) ;
}
// Get non-instruct impersonation line
if ( ! isInstruct && isImpersonate && tokens _already _generated === 0 ) {
const name = is _pygmalion ? 'You' : name1 ;
if ( ! mesSendString . endsWith ( '\n' ) ) {
mesSendString += '\n' ;
}
mesSendString += name + ':' ;
}
// Add character's name
if ( ! isInstruct && force _name2 && tokens _already _generated === 0 ) {
if ( ! mesSendString . endsWith ( '\n' ) ) {
mesSendString += '\n' ;
}
// Add a leading space to the prompt bias if applicable
if ( ! promptBias || promptBias . length === 0 ) {
console . debug ( "No prompt bias was found." ) ;
mesSendString += ` ${ name2 } : ` ;
} else if ( promptBias . startsWith ( ' ' ) ) {
console . debug ( ` A prompt bias with a leading space was found: ${ promptBias } ` ) ;
mesSendString += ` ${ name2 } : ${ promptBias } `
} else {
console . debug ( ` A prompt bias was found: ${ promptBias } ` ) ;
mesSendString += ` ${ name2 } : ${ promptBias } ` ;
}
} else if ( power _user . user _prompt _bias && ! isImpersonate && ! isInstruct ) {
console . debug ( ` A prompt bias was found without character's name appended: ${ promptBias } ` ) ;
mesSendString += substituteParams ( power _user . user _prompt _bias ) ;
}
return mesSendString ;
}
function checkPromtSize ( ) {
console . debug ( '---checking Prompt size' ) ;
setPromtString ( ) ;
const prompt = [
storyString ,
mesExmString ,
mesSendString ,
generatedPromtCache ,
allAnchors ,
quiet _prompt ,
] . join ( '' ) . replace ( /\r/gm , '' ) ;
let thisPromtContextSize = getTokenCount ( prompt , power _user . token _padding ) ;
if ( thisPromtContextSize > this _max _context ) { //if the prepared prompt is larger than the max context size...
if ( count _exm _add > 0 ) { // ..and we have example mesages..
count _exm _add -- ; // remove the example messages...
checkPromtSize ( ) ; // and try agin...
} else if ( mesSend . length > 0 ) { // if the chat history is longer than 0
mesSend . shift ( ) ; // remove the first (oldest) chat entry..
checkPromtSize ( ) ; // and check size again..
} else {
//end
console . debug ( ` ---mesSend.length = ${ mesSend . length } ` ) ;
}
}
}
if ( generatedPromtCache . length > 0 && main _api !== 'openai' ) {
console . debug ( '---Generated Prompt Cache length: ' + generatedPromtCache . length ) ;
checkPromtSize ( ) ;
} else {
console . debug ( '---calling setPromtString ' + generatedPromtCache . length )
setPromtString ( ) ;
}
2023-08-04 03:22:46 +02:00
// add chat preamble
mesSendString = addChatsPreamble ( mesSendString ) ;
2023-07-20 19:32:15 +02:00
// add a custom dingus (if defined)
2023-08-04 03:22:46 +02:00
mesSendString = addChatsSeparator ( mesSendString ) ;
2023-07-20 19:32:15 +02:00
let finalPromt =
storyString +
afterScenarioAnchor +
mesExmString +
mesSendString +
generatedPromtCache ;
if ( zeroDepthAnchor && zeroDepthAnchor . length ) {
if ( ! isMultigenEnabled ( ) || tokens _already _generated == 0 ) {
finalPromt = appendZeroDepthAnchor ( force _name2 , zeroDepthAnchor , finalPromt ) ;
}
}
finalPromt = finalPromt . replace ( /\r/gm , '' ) ;
if ( power _user . collapse _newlines ) {
finalPromt = collapseNewlines ( finalPromt ) ;
}
let this _amount _gen = parseInt ( amount _gen ) ; // how many tokens the AI will be requested to generate
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 = [ ] ;
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 = {
prompt : finalPromt ,
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 ;
generate _data = getKoboldGenerationData ( finalPromt , this _settings , this _amount _gen , maxContext , isImpersonate , type ) ;
}
}
else if ( main _api == 'textgenerationwebui' ) {
generate _data = getTextGenGenerationData ( finalPromt , this _amount _gen , isImpersonate ) ;
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-02 21:35:05 +02:00
generate _data = getNovelGenerationData ( finalPromt , this _settings , this _amount _gen , isImpersonate ) ;
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-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 ] ,
rawPrompt : generate _data . prompt ,
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 ,
mesSendString : mesSendString ,
generatedPromtCache : generatedPromtCache ,
promptBias : promptBias ,
finalPromt : finalPromt ,
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 ) ;
console . log ( ` pushed prompt bits to itemizedPrompts array. Length is now: ${ itemizedPrompts . length } ` ) ;
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' ) {
generateHorde ( finalPromt , generate _data , abortController . signal ) . then ( onSuccess ) . catch ( onError ) ;
}
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 ) {
streamingProcessor . onFinishStreaming ( streamingProcessor . messageId , getMessage ) ;
streamingProcessor = null ;
}
}
async function onSuccess ( data ) {
2023-08-14 23:01:55 +02:00
if ( data . error == 'dryRun' ) {
generatedPromtCache = '' ;
resolve ( ) ;
return ;
}
2023-07-20 19:32:15 +02:00
hideStopButton ( ) ;
is _send _press = false ;
if ( ! data . error ) {
//const getData = await response.json();
let getMessage = extractMessageFromData ( data ) ;
let title = extractTitleFromData ( data ) ;
kobold _horde _model = title ;
//Pygmalion run again
// 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" ) ;
( { type , getMessage } = saveReply ( type , getMessage , this _mes _is _name , title ) ) ;
}
else {
console . debug ( "Should append message" ) ;
( { type , getMessage } = saveReply ( 'append' , getMessage , this _mes _is _name , title ) ) ;
}
} 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 ;
generatedPromtCache = "" ;
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' ) ;
generatedPromtCache = "" ;
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' ) {
( { type , getMessage } = saveReply ( type , getMessage , this _mes _is _name , title ) ) ;
}
else {
( { type , getMessage } = saveReply ( 'appendFinal' , getMessage , this _mes _is _name , title ) ) ;
}
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
}
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' ) ;
swipe _right ( ) ;
return
}
}
} else {
generatedPromtCache = '' ;
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' ) ;
saveChatConditional ( ) ;
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 } ;
}
function formatMessageHistoryItem ( chatItem , isInstruct ) {
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 ) {
textResult = formatInstructModeChat ( itemName , chatItem . mes , chatItem . is _user , isNarratorType , chatItem . force _avatar , name1 , name2 ) ;
}
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' ] = { } ;
// 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 , '' ) ;
addOneMessage ( chat [ chat . length - 1 ] ) ;
// Wait for all handlers to finish before continuing with the prompt
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
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 ) ;
if ( nai _settings . model _novel == 'krake-v2' || nai _settings . model _novel == 'euterpe-v2' ) {
this _max _context = Math . min ( max _context , 2048 ) ;
}
2023-08-12 13:21:05 +02:00
if ( nai _settings . model _novel == 'clio-v1' ) {
2023-07-20 19:32:15 +02:00
this _max _context = Math . min ( max _context , 8192 ) ;
}
2023-08-12 13:21:05 +02:00
if ( nai _settings . model _novel == 'kayra-v1' ) {
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 ) {
const total = Object . values ( counts ) . filter ( x => ! Number . isNaN ( x ) ) . reduce ( ( acc , val ) => acc + val , 0 ) ;
thisPromptBits . push ( {
2023-06-02 19:17:02 +02:00
oaiStartTokens : Object . entries ( counts ) ? . [ 0 ] ? . [ 1 ] ? ? 0 ,
oaiPromptTokens : Object . entries ( counts ) ? . [ 1 ] ? . [ 1 ] ? ? 0 ,
oaiBiasTokens : Object . entries ( counts ) ? . [ 2 ] ? . [ 1 ] ? ? 0 ,
oaiNudgeTokens : Object . entries ( counts ) ? . [ 3 ] ? . [ 1 ] ? ? 0 ,
oaiJailbreakTokens : Object . entries ( counts ) ? . [ 4 ] ? . [ 1 ] ? ? 0 ,
oaiImpersonateTokens : Object . entries ( counts ) ? . [ 5 ] ? . [ 1 ] ? ? 0 ,
oaiExamplesTokens : Object . entries ( counts ) ? . [ 6 ] ? . [ 1 ] ? ? 0 ,
oaiConversationTokens : Object . entries ( counts ) ? . [ 7 ] ? . [ 1 ] ? ? 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'
? 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-17 21:47:34 +02:00
if ( main _api === 'novel' ) {
return '***\n' + mesSendString ;
2023-08-04 03:22:46 +02:00
}
2023-08-17 21:47:34 +02:00
else if ( power _user . context . chat _start ) {
return 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
}
}
function appendZeroDepthAnchor ( force _name2 , zeroDepthAnchor , finalPromt ) {
const trimBothEnds = ! force _name2 && ! is _pygmalion ;
let trimmedPrompt = ( trimBothEnds ? zeroDepthAnchor . trim ( ) : zeroDepthAnchor . trimEnd ( ) ) ;
if ( trimBothEnds && ! finalPromt . endsWith ( '\n' ) ) {
finalPromt += '\n' ;
}
finalPromt += trimmedPrompt ;
if ( force _name2 || is _pygmalion ) {
finalPromt += ' ' ;
}
return finalPromt ;
}
function getMultigenAmount ( ) {
let this _amount _gen = parseInt ( amount _gen ) ;
if ( tokens _already _generated === 0 ) {
// if the max gen setting is > 50...(
if ( parseInt ( amount _gen ) >= power _user . multigen _first _chunk ) {
// 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.
this _amount _gen = parseInt ( amount _gen ) ;
}
}
// if we already received some generated text...
else {
// if the remaining tokens to be made is less than next potential cycle count
if ( parseInt ( amount _gen ) - tokens _already _generated < power _user . multigen _next _chunks ) {
// subtract already generated amount from the desired max gen amount
this _amount _gen = parseInt ( amount _gen ) - tokens _already _generated ;
}
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;
var oaiStartTokens = itemizedPrompts [ thisPromptSet ] . oaiStartTokens ;
var oaiPromptTokens = itemizedPrompts [ thisPromptSet ] . oaiPromptTokens - worldInfoStringTokens - afterScenarioAnchorTokens ;
var ActualChatHistoryTokens = itemizedPrompts [ thisPromptSet ] . oaiConversationTokens ;
var examplesStringTokens = itemizedPrompts [ thisPromptSet ] . oaiExamplesTokens ;
var oaiBiasTokens = itemizedPrompts [ thisPromptSet ] . oaiBiasTokens ;
var oaiJailbreakTokens = itemizedPrompts [ thisPromptSet ] . oaiJailbreakTokens ;
var oaiNudgeTokens = itemizedPrompts [ thisPromptSet ] . oaiNudgeTokens ;
var oaiImpersonateTokens = itemizedPrompts [ thisPromptSet ] . oaiImpersonateTokens ;
var finalPromptTokens =
oaiStartTokens +
oaiPromptTokens +
oaiBiasTokens +
oaiImpersonateTokens +
oaiJailbreakTokens +
oaiNudgeTokens +
ActualChatHistoryTokens +
//charDescriptionTokens +
//charPersonalityTokens +
//allAnchorsTokens +
worldInfoStringTokens +
afterScenarioAnchorTokens +
examplesStringTokens ;
// 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');
var finalPromptTokens = getTokenCount ( itemizedPrompts [ thisPromptSet ] . finalPromt ) ;
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 ; //{{}}
//- thisPrompt_padding; //not sure this way of calculating is correct, but the math results in same value as 'finalPromt'
}
if ( this _main _api == 'openai' ) {
//console.log('-- applying % on OAI tokens');
var oaiStartTokensPercentage = ( ( oaiStartTokens / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
var storyStringTokensPercentage = ( ( ( examplesStringTokens + afterScenarioAnchorTokens + oaiPromptTokens ) / ( finalPromptTokens ) ) * 100 ) . toFixed ( 2 ) ;
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 ( ) } ) ` ;
var oaiSystemTokens = oaiImpersonateTokens + oaiJailbreakTokens + oaiNudgeTokens + oaiStartTokens ;
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 ( ) ;
}
if ( this _main _api == 'openai' ) {
//console.log('-- calling popup for OAI tokens');
callPopup (
`
< h3 class = "flex-container justifyCenter alignitemscenter" >
Prompt Itemization
< div id = "showRawPrompt" class = "fa-solid fa-square-poll-horizontal menu_button" > < / d i v >
< / h 3 >
Tokenizer : $ { selectedTokenizer } < br >
API Used : $ { this _main _api } < br >
< span class = "tokenItemizingSubclass" >
Only the white numbers really matter . All numbers are estimates .
Grey color items may not have been included in the context due to certain prompt format settings .
< / s p a n >
< hr >
< div class = "justifyLeft" >
< div class = "flex-container" >
< div class = "flex-container flex1 flexFlowColumns flexNoGap wide50p tokenGraph" >
< div class = "wide100p" style = "background-color: grey; height: ${oaiSystemTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: salmon; height: ${oaiStartTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: indianred; height: ${storyStringTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: gold; height: ${worldInfoStringTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: palegreen; height: ${ActualChatHistoryTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: cornflowerblue; height: ${allAnchorsTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: mediumpurple; height: ${promptBiasTokensPercentage}%;" > < / d i v >
< / d i v >
< div class = "flex-container wide50p" >
< div class = "wide100p flex-container flexNoGap flexFlowColumn" >
< div class = "flex-container wide100p" >
< div class = "flex1" style = "color: grey;" > System Info : < / d i v >
< div class = "" > $ { oaiSystemTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Chat Start : < / d i v >
< div class = "tokenItemizingSubclass" > $ { oaiStartTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Jailbreak : < / d i v >
< div class = "tokenItemizingSubclass" > $ { oaiJailbreakTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- NSFW : < / d i v >
< div class = "tokenItemizingSubclass" > ? ? < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Nudge : < / d i v >
< div class = "tokenItemizingSubclass" > $ { oaiNudgeTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Impersonate : < / d i v >
< div class = "tokenItemizingSubclass" > $ { oaiImpersonateTokens } < / d i v >
< / d i v >
< / d i v >
< div class = "wide100p flex-container flexNoGap flexFlowColumn" >
< div class = "flex-container wide100p" >
< div class = "flex1" style = "color: indianred;" > Prompt Tokens : < / d i v >
< div class = "" > $ { oaiPromptTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Description : < / d i v >
< div class = "tokenItemizingSubclass" > $ { charDescriptionTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Personality : < / d i v >
< div class = "tokenItemizingSubclass" > $ { charPersonalityTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Scenario : < / d i v >
< div class = "tokenItemizingSubclass" > $ { scenarioTextTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Examples : < / d i v >
< div class = "tokenItemizingSubclass" > $ { examplesStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- User Persona : < / d i v >
< div class = "tokenItemizingSubclass" > $ { userPersonaStringTokens } < / d i v >
< / d i v >
< / d i v >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: gold;" > World Info : < / d i v >
< div class = "" > $ { worldInfoStringTokens } < / d i v >
< / d i v >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: palegreen;" > Chat History : < / d i v >
< div class = "" > $ { ActualChatHistoryTokens } < / d i v >
< / d i v >
< div class = "wide100p flex-container flexNoGap flexFlowColumn" >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: cornflowerblue;" > Extensions : < / d i v >
< div class = "" > $ { allAnchorsTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Summarize : < / d i v >
< div class = "tokenItemizingSubclass" > $ { summarizeStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Author ' s Note : < / d i v >
< div class = "tokenItemizingSubclass" > $ { authorsNoteStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Smart Context : < / d i v >
< div class = "tokenItemizingSubclass" > $ { smartContextStringTokens } < / d i v >
< / d i v >
< / d i v >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: mediumpurple;" > { { } } Bias : < / d i v > < d i v c l a s s = " " > $ { o a i B i a s T o k e n s } < / d i v >
< / d i v >
< / d i v >
< / d i v >
< hr >
< div class = "wide100p flex-container flexFlowColumns" >
< div class = "flex-container wide100p" >
< div class = "flex1" > Total Tokens in Prompt : < / d i v > < d i v c l a s s = " " > $ { f i n a l P r o m p t T o k e n s } < / d i v >
< / d i v >
< div class = "flex-container wide100p" >
< div class = "flex1" > Max Context ( Context Size - Response Length ) : < / d i v > < d i v c l a s s = " " > $ { t h i s P r o m p t _ m a x _ c o n t e x t } < / d i v >
< / d i v >
< / d i v >
< / d i v >
< hr >
` , 'text'
) ;
} else {
//console.log('-- calling popup for non-OAI tokens');
callPopup (
`
< h3 class = "flex-container justifyCenter alignitemscenter" >
Prompt Itemization
< div id = "showRawPrompt" class = "fa-solid fa-square-poll-horizontal menu_button" > < / d i v >
< / h 3 >
Tokenizer : $ { selectedTokenizer } < br >
API Used : $ { this _main _api } < br >
< span class = "tokenItemizingSubclass" >
Only the white numbers really matter . All numbers are estimates .
Grey color items may not have been included in the context due to certain prompt format settings .
< / s p a n >
< hr >
< div class = "justifyLeft" >
< div class = "flex-container" >
< div class = "flex-container flex1 flexFlowColumns flexNoGap wide50p tokenGraph" >
< div class = "wide100p" style = "background-color: indianred; height: ${storyStringTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: gold; height: ${worldInfoStringTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: palegreen; height: ${ActualChatHistoryTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: cornflowerblue; height: ${allAnchorsTokensPercentage}%;" > < / d i v >
< div class = "wide100p" style = "background-color: mediumpurple; height: ${promptBiasTokensPercentage}%;" > < / d i v >
< / d i v >
< div class = "flex-container wide50p" >
< div class = "wide100p flex-container flexNoGap flexFlowColumn" >
< div class = "flex-container wide100p" >
< div class = "flex1" style = "color: indianred;" > Character Definitions : < / d i v >
< div class = "" > $ { storyStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Description : < / d i v >
< div class = "tokenItemizingSubclass" > $ { charDescriptionTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Personality : < / d i v >
< div class = "tokenItemizingSubclass" > $ { charPersonalityTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Scenario : < / d i v >
< div class = "tokenItemizingSubclass" > $ { scenarioTextTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Examples : < / d i v >
< div class = "tokenItemizingSubclass" > $ { examplesStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- User Persona : < / d i v >
< div class = "tokenItemizingSubclass" > $ { userPersonaStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- System Prompt ( Instruct ) : < / d i v >
< div class = "tokenItemizingSubclass" > $ { instructionTokens } < / d i v >
< / d i v >
< / d i v >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: gold;" > World Info : < / d i v >
< div class = "" > $ { worldInfoStringTokens } < / d i v >
< / d i v >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: palegreen;" > Chat History : < / d i v >
< div class = "" > $ { ActualChatHistoryTokens } < / d i v >
< / d i v >
< div class = "wide100p flex-container flexNoGap flexFlowColumn" >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: cornflowerblue;" > Extensions : < / d i v >
< div class = "" > $ { allAnchorsTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Summarize : < / d i v >
< div class = "tokenItemizingSubclass" > $ { summarizeStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Author ' s Note : < / d i v >
< div class = "tokenItemizingSubclass" > $ { authorsNoteStringTokens } < / d i v >
< / d i v >
< div class = "flex-container " >
< div class = " flex1 tokenItemizingSubclass" > -- Smart Context : < / d i v >
< div class = "tokenItemizingSubclass" > $ { smartContextStringTokens } < / d i v >
< / d i v >
< / d i v >
< div class = "wide100p flex-container" >
< div class = "flex1" style = "color: mediumpurple;" > { { } } Bias : < / d i v > < d i v c l a s s = " " > $ { p r o m p t B i a s T o k e n s } < / d i v >
< / d i v >
< / d i v >
< / d i v >
< hr >
< div class = "wide100p flex-container flexFlowColumns" >
< div class = "flex-container wide100p" >
< div class = "flex1" > Total Tokens in Prompt : < / d i v > < d i v c l a s s = " " > $ { t o t a l T o k e n s I n P r o m p t } < / d i v >
< / d i v >
<!-- < div class = "flex1" > finalPromt : < / d i v > < d i v c l a s s = " " > $ { f i n a l P r o m p t T o k e n s } < / d i v > - - >
< div class = "flex-container wide100p" >
< div class = "flex1" > Max Context ( Context Size - Response Length ) : < / d i v > < d i v c l a s s = " " > $ { t h i s P r o m p t _ m a x _ c o n t e x t } < / d i v >
< / d i v >
< div class = "flex-container wide100p" >
< div class = "flex1" > - Padding : < / d i v > < d i v c l a s s = " " > $ { t h i s P r o m p t _ p a d d i n g } < / d i v >
< / d i v >
< div class = "flex-container wide100p" >
< div class = "flex1" > Actual Max Context Allowed : < / d i v > < d i v c l a s s = " " > $ { t h i s P r o m p t _ m a x _ c o n t e x t - t h i s P r o m p t _ p a d d i n g } < / d i v >
< / d i v >
< / d i v >
< / d i v >
< hr >
` , 'text'
) ;
}
}
function setInContextMessages ( lastmsg , type ) {
$ ( "#chat .mes" ) . removeClass ( 'lastInContext' ) ;
if ( type === 'swipe' || type === 'regenerate' || type === 'continue' ) {
lastmsg ++ ;
}
$ ( '#chat .mes:not([is_system="true"])' ) . eq ( - lastmsg ) . addClass ( 'lastInContext' ) ;
}
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
const nameString = isImpersonate ? ` ${ name2 } : ` : ( is _pygmalion ? 'You:' : ` ${ name1 } : ` ) ;
// 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..
const notReachedMax = tokens _already _generated < parseInt ( amount _gen ) ;
//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 ) {
getMessage = end _trim _to _sentence ( getMessage , power _user . include _newline ) ;
}
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 , "" ) ;
if ( is _pygmalion ) {
getMessage = getMessage . replace ( /<USER>/g , name1 ) ;
getMessage = getMessage . replace ( /<BOT>/g , name2 ) ;
getMessage = getMessage . replace ( /You:/g , name1 + ':' ) ;
}
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 ) ) ;
}
}
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 ( ) ;
}
const stoppingStrings = getStoppingStrings ( isImpersonate , false ) ;
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 ) {
getMessage = fixMarkdown ( getMessage ) ;
}
return getMessage ;
}
function saveReply ( type , getMessage , this _mes _is _name , title ) {
if ( type != 'append' && type != 'continue' && type != 'appendFinal' && chat . length && ( chat [ chat . length - 1 ] [ 'swipe_id' ] === undefined ||
chat [ chat . length - 1 ] [ 'is_user' ] ) ) {
type = 'normal' ;
}
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 ( ) ;
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
} 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 ( ) ;
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
} 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 ( ) ;
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
} 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 ;
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 ] ) ;
addOneMessage ( chat [ chat . length - 1 ] ) ;
}
const item = chat [ chat . length - 1 ] ;
if ( item [ "swipe_info" ] === undefined ) {
item [ "swipe_info" ] = [ ] ;
}
if ( item [ "swipe_id" ] !== undefined ) {
item [ "swipes" ] [ item [ "swipes" ] . length - 1 ] = item [ "mes" ] ;
item [ "swipe_info" ] [ item [ "swipes" ] . length - 1 ] = {
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 ;
$ ( "#send_but" ) . css ( "display" , "flex" ) ;
$ ( "#send_textarea" ) . attr ( "disabled" , false ) ;
$ ( '.mes_buttons:last' ) . show ( ) ;
hideStopButton ( ) ;
}
export function deactivateSendButtons ( ) {
$ ( "#send_but" ) . css ( "display" , "none" ) ;
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 ;
}
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 ) ;
}
}
}
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 ( ) {
if ( $ ( this ) . attr ( "is_system" ) == 'true' ) {
return ;
}
if ( $ ( this ) . attr ( "is_user" ) == 'true' ) {
return ;
}
if ( $ ( this ) . attr ( "ch_name" ) == formData . get ( 'ch_name' ) ) {
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-19 14:58:17 +02:00
saveChatDebounced ( ) ;
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 ) {
const firstMes = characters [ this _chid ] . first _mes || default _ch _mes ;
const alternateGreetings = characters [ this _chid ] ? . data ? . alternate _greetings ;
chat [ 0 ] = {
name : name2 ,
is _user : false ,
is _name : true ,
send _date : getMessageTimeStamp ( ) ,
mes : getRegexedString ( firstMes , regex _placement . AI _OUTPUT ) ,
} ;
if ( Array . isArray ( alternateGreetings ) && alternateGreetings . length > 0 ) {
chat [ 0 ] [ 'swipe_id' ] = 0 ;
chat [ 0 ] [ 'swipes' ] = [ chat [ 0 ] [ 'mes' ] ] . concat (
alternateGreetings . map (
( greeting ) => substituteParams ( getRegexedString ( greeting , regex _placement . AI _OUTPUT ) )
)
) ;
chat [ 0 ] [ 'swipe_info' ] = [ ] ;
}
}
printMessages ( ) ;
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 ) ) ;
}
}
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
}
////////////////////////////////////////////////////
async function getUserAvatars ( ) {
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 setPersonaDescription ( ) {
$ ( "#persona_description" ) . val ( power _user . persona _description ) ;
$ ( "#persona_description_position" )
. val ( power _user . persona _description _position )
. find ( ` option[value=' ${ power _user . persona _description _position } '] ` )
. attr ( "selected" , true ) ;
}
function onPersonaDescriptionPositionInput ( ) {
power _user . persona _description _position = Number (
$ ( "#persona_description_position" ) . find ( ":selected" ) . val ( )
) ;
if ( power _user . personas [ user _avatar ] ) {
let object = power _user . persona _descriptions [ user _avatar ] ;
if ( ! object ) {
object = {
description : power _user . persona _description ,
position : power _user . persona _description _position ,
} ;
power _user . persona _descriptions [ user _avatar ] = object ;
}
object . position = power _user . persona _description _position ;
}
saveSettingsDebounced ( ) ;
}
function onPersonaDescriptionInput ( ) {
power _user . persona _description = $ ( "#persona_description" ) . val ( ) ;
if ( power _user . personas [ user _avatar ] ) {
let object = power _user . persona _descriptions [ user _avatar ] ;
if ( ! object ) {
object = {
description : power _user . persona _description ,
position : Number ( $ ( "#persona_description_position" ) . find ( ":selected" ) . val ( ) ) ,
} ;
power _user . persona _descriptions [ user _avatar ] = object ;
}
object . description = power _user . persona _description ;
}
saveSettingsDebounced ( ) ;
}
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" ) ;
}
export function autoSelectPersona ( name ) {
for ( const [ key , value ] of Object . entries ( power _user . personas ) ) {
if ( value === name ) {
console . log ( ` Auto-selecting persona ${ key } for name ${ name } ` ) ;
$ ( ` .avatar[imgfile=" ${ key } "] ` ) . trigger ( 'click' ) ;
return ;
}
}
}
async function bindUserNameToPersona ( ) {
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . warn ( 'No avatar id found' ) ;
return ;
}
const existingPersona = power _user . personas [ avatarId ] ;
const personaName = await callPopup ( '<h3>Enter a name for this persona:</h3>(If empty name is provided, this will unbind the name from this avatar)' , 'input' , existingPersona || '' ) ;
// If the user clicked cancel, don't do anything
if ( personaName === false ) {
return ;
}
if ( personaName . length > 0 ) {
// If the user clicked ok and entered a name, bind the name to the persona
console . log ( ` Binding persona ${ avatarId } to name ${ personaName } ` ) ;
power _user . personas [ avatarId ] = personaName ;
const descriptor = power _user . persona _descriptions [ avatarId ] ;
const isCurrentPersona = avatarId === user _avatar ;
// Create a description object if it doesn't exist
if ( ! descriptor ) {
// If the user is currently using this persona, set the description to the current description
power _user . persona _descriptions [ avatarId ] = {
description : isCurrentPersona ? power _user . persona _description : '' ,
position : isCurrentPersona ? power _user . persona _description _position : persona _description _positions . BEFORE _CHAR ,
} ;
}
// If the user is currently using this persona, update the name
if ( isCurrentPersona ) {
console . log ( ` Auto-updating user name to ${ personaName } ` ) ;
setUserName ( personaName ) ;
}
} else {
// If the user clicked ok, but didn't enter a name, delete the persona
console . log ( ` Unbinding persona ${ avatarId } ` ) ;
delete power _user . personas [ avatarId ] ;
delete power _user . persona _descriptions [ avatarId ] ;
}
saveSettingsDebounced ( ) ;
await getUserAvatars ( ) ;
setPersonaDescription ( ) ;
}
async function createDummyPersona ( ) {
const fetchResult = await fetch ( default _avatar ) ;
const blob = await fetchResult . blob ( ) ;
const file = new File ( [ blob ] , "avatar.png" , { type : "image/png" } ) ;
const formData = new FormData ( ) ;
formData . append ( "avatar" , file ) ;
jQuery . ajax ( {
type : "POST" ,
url : "/uploaduseravatar" ,
data : formData ,
beforeSend : ( ) => { } ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( data ) {
await getUserAvatars ( ) ;
} ,
} ) ;
}
function updateUserLockIcon ( ) {
const hasLock = ! ! chat _metadata [ 'persona' ] ;
$ ( '#lock_user_name' ) . toggleClass ( 'fa-unlock' , ! hasLock ) ;
$ ( '#lock_user_name' ) . toggleClass ( 'fa-lock' , hasLock ) ;
}
function setUserAvatar ( ) {
user _avatar = $ ( this ) . attr ( "imgfile" ) ;
reloadUserAvatar ( ) ;
saveSettingsDebounced ( ) ;
highlightSelectedAvatar ( ) ;
const personaName = power _user . personas [ user _avatar ] ;
if ( personaName && name1 !== personaName ) {
const lockedPersona = chat _metadata [ 'persona' ] ;
2023-08-03 13:24:45 +02:00
if ( lockedPersona && lockedPersona !== user _avatar && power _user . persona _show _notifications ) {
2023-07-20 19:32:15 +02:00
toastr . info (
` To permanently set " ${ personaName } " as the selected persona, unlock and relock it using the "Lock" button. Otherwise, the selection resets upon reloading the chat. ` ,
` This chat is locked to a different persona ( ${ power _user . personas [ lockedPersona ] } ). ` ,
{ timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ,
) ;
}
setUserName ( personaName ) ;
const descriptor = power _user . persona _descriptions [ user _avatar ] ;
if ( descriptor ) {
power _user . persona _description = descriptor . description ;
power _user . persona _description _position = descriptor . position ;
} else {
power _user . persona _description = '' ;
power _user . persona _description _position = persona _description _positions . BEFORE _CHAR ;
power _user . persona _descriptions [ user _avatar ] = { description : '' , position : persona _description _positions . BEFORE _CHAR } ;
}
setPersonaDescription ( ) ;
}
}
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" ) ;
}
async function setDefaultPersona ( ) {
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . warn ( 'No avatar id found' ) ;
return ;
}
const currentDefault = power _user . default _persona ;
if ( power _user . personas [ avatarId ] === undefined ) {
console . warn ( ` No persona name found for avatar ${ avatarId } ` ) ;
toastr . warning ( 'You must bind a name to this persona before you can set it as the default.' , 'Persona name not set' ) ;
return ;
}
const personaName = power _user . personas [ avatarId ] ;
if ( avatarId === currentDefault ) {
const confirm = await callPopup ( 'Are you sure you want to remove the default persona?' , 'confirm' ) ;
if ( ! confirm ) {
console . debug ( 'User cancelled removing default persona' ) ;
return ;
}
console . log ( ` Removing default persona ${ avatarId } ` ) ;
2023-08-03 13:24:45 +02:00
if ( power _user . persona _show _notifications ) {
toastr . info ( 'This persona will no longer be used by default when you open a new chat.' , ` Default persona removed ` ) ;
}
2023-07-20 19:32:15 +02:00
delete power _user . default _persona ;
} else {
const confirm = await callPopup ( ` <h3>Are you sure you want to set " ${ personaName } " as the default persona?</h3>
This name and avatar will be used for all new chats , as well as existing chats where the user persona is not locked . ` , 'confirm');
if ( ! confirm ) {
console . debug ( 'User cancelled setting default persona' ) ;
return ;
}
power _user . default _persona = avatarId ;
2023-08-03 13:24:45 +02:00
if ( power _user . persona _show _notifications ) {
toastr . success ( 'This persona will be used by default when you open a new chat.' , ` Default persona set to ${ personaName } ` ) ;
}
2023-07-20 19:32:15 +02:00
}
saveSettingsDebounced ( ) ;
await getUserAvatars ( ) ;
}
async function deleteUserAvatar ( ) {
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . warn ( 'No avatar id found' ) ;
return ;
}
if ( avatarId == user _avatar ) {
console . warn ( ` User tried to delete their current avatar ${ avatarId } ` ) ;
toastr . warning ( 'You cannot delete the avatar you are currently using' , 'Warning' ) ;
return ;
}
const confirm = await callPopup ( '<h3>Are you sure you want to delete this avatar?</h3>All information associated with its linked persona will be lost.' , 'confirm' ) ;
if ( ! confirm ) {
console . debug ( 'User cancelled deleting avatar' ) ;
return ;
}
const request = await fetch ( "/deleteuseravatar" , {
method : "POST" ,
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
"avatar" : avatarId ,
} ) ,
} ) ;
if ( request . ok ) {
console . log ( ` Deleted avatar ${ avatarId } ` ) ;
delete power _user . personas [ avatarId ] ;
delete power _user . persona _descriptions [ avatarId ] ;
if ( avatarId === power _user . default _persona ) {
toastr . warning ( 'The default persona was deleted. You will need to set a new default persona.' , 'Default persona deleted' ) ;
power _user . default _persona = null ;
}
if ( avatarId === chat _metadata [ 'persona' ] ) {
toastr . warning ( 'The locked persona was deleted. You will need to set a new persona for this chat.' , 'Persona deleted' ) ;
delete chat _metadata [ 'persona' ] ;
saveMetadata ( ) ;
}
saveSettingsDebounced ( ) ;
await getUserAvatars ( ) ;
updateUserLockIcon ( ) ;
}
}
function lockUserNameToChat ( ) {
if ( chat _metadata [ 'persona' ] ) {
console . log ( ` Unlocking persona for this chat ${ chat _metadata [ 'persona' ] } ` ) ;
delete chat _metadata [ 'persona' ] ;
saveMetadata ( ) ;
2023-08-03 13:24:45 +02:00
if ( power _user . persona _show _notifications ) {
toastr . info ( 'User persona is now unlocked for this chat. Click the "Lock" again to revert.' , 'Persona unlocked' ) ;
}
2023-07-20 19:32:15 +02:00
updateUserLockIcon ( ) ;
return ;
}
if ( ! ( user _avatar in power _user . personas ) ) {
console . log ( ` Creating a new persona ${ user _avatar } ` ) ;
2023-08-03 13:24:45 +02:00
if ( power _user . persona _show _notifications ) {
toastr . info (
'Creating a new persona for currently selected user name and avatar...' ,
'Persona not set for this avatar' ,
{ timeOut : 10000 , extendedTimeOut : 20000 , } ,
) ;
}
2023-07-20 19:32:15 +02:00
power _user . personas [ user _avatar ] = name1 ;
power _user . persona _descriptions [ user _avatar ] = { description : '' , position : persona _description _positions . BEFORE _CHAR } ;
}
chat _metadata [ 'persona' ] = user _avatar ;
saveMetadata ( ) ;
saveSettingsDebounced ( ) ;
console . log ( ` Locking persona for this chat ${ user _avatar } ` ) ;
2023-08-03 13:24:45 +02:00
if ( power _user . persona _show _notifications ) {
toastr . success ( ` User persona is locked to ${ name1 } in this chat ` ) ;
}
2023-07-20 19:32:15 +02:00
updateUserLockIcon ( ) ;
}
eventSource . on ( event _types . CHAT _CHANGED , ( ) => {
// Define a persona for this chat
let chatPersona = '' ;
if ( chat _metadata [ 'persona' ] ) {
// If persona is locked in chat metadata, select it
console . log ( ` Using locked persona ${ chat _metadata [ 'persona' ] } ` ) ;
chatPersona = chat _metadata [ 'persona' ] ;
} else if ( power _user . default _persona ) {
// If default persona is set, select it
console . log ( ` Using default persona ${ power _user . default _persona } ` ) ;
chatPersona = power _user . default _persona ;
}
// No persona set: user current settings
if ( ! chatPersona ) {
console . debug ( 'No default or locked persona set for this chat' ) ;
return ;
}
// Find the avatar file
const personaAvatar = $ ( ` .avatar[imgfile=" ${ chatPersona } "] ` ) . trigger ( 'click' ) ;
// Avatar missing (persona deleted)
if ( chat _metadata [ 'persona' ] && personaAvatar . length == 0 ) {
console . warn ( 'Persona avatar not found, unlocking persona' ) ;
delete chat _metadata [ 'persona' ] ;
updateUserLockIcon ( ) ;
return ;
}
// Default persona missing
if ( power _user . default _persona && personaAvatar . length == 0 ) {
console . warn ( 'Default persona avatar not found, clearing default persona' ) ;
power _user . default _persona = null ;
saveSettingsDebounced ( ) ;
return ;
}
// Persona avatar found, select it
personaAvatar . trigger ( 'click' ) ;
updateUserLockIcon ( ) ;
} ) ;
2023-07-21 15:42:18 +02:00
async function doOnboarding ( avatarId ) {
const template = $ ( '#onboarding_template .onboarding' ) ;
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 : '' ,
position : persona _description _positions . BEFORE _CHAR ,
} ;
}
}
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
loadOpenAISettings ( data , settings ) ;
// 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 ) {
//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-07-20 19:32:15 +02:00
... oai _settings ,
} , 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,
success : function ( data ) {
//online_status = data.result;
eventSource . emit ( event _types . SETTINGS _UPDATED ) ;
if ( type == "change_name" ) {
clearChat ( ) ;
printMessages ( ) ;
}
} ,
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 ) {
max _context = preset . max _length ;
const needsUnlock = max _context > MAX _CONTEXT _DEFAULT ;
$ ( '#max_context_unlocked' ) . prop ( 'checked' , needsUnlock ) . trigger ( 'change' ) ;
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
function setCharacterBlockHeight ( ) {
const $children = $ ( "#rm_print_characters_block" ) . children ( ) ;
const originalHeight = $children . length * $children . find ( ':visible' ) . first ( ) . outerHeight ( ) ;
$ ( "#rm_print_characters_block" ) . css ( 'height' , originalHeight ) ;
//show and hide charlist divs on pageload (causes load lag)
//$children.each(function () { setCharListVisible($(this)) });
//delay timer to allow for charlist to populate,
//should be set to an onload for rm_print_characters or windows?
}
// 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-19 14:58:17 +02:00
* @ returns { 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-19 14:58:17 +02:00
* @ returns { 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-08-19 05:48:08 +02:00
return chatContent && Object . values ( chatContent ) . some ( message => message . mes . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) ) ) ;
} ) ;
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 ) {
const data = { } ;
jQuery . ajax ( {
type : "POST" , //
url : "/getstatus_novelai" , //
data : JSON . stringify ( data ) ,
beforeSend : function ( ) {
} ,
cache : false ,
dataType : "json" ,
contentType : "application/json" ,
success : function ( data ) {
if ( data . error != true ) {
2023-08-10 18:15:52 +02:00
setNovelData ( data ) ;
2023-08-12 13:21:05 +02:00
online _status = ` ${ getNovelTier ( data . tier ) } ` ;
2023-07-20 19:32:15 +02:00
}
resultCheckStatusNovel ( ) ;
} ,
error : function ( jqXHR , exception ) {
online _status = "no_connection" ;
console . log ( exception ) ;
console . log ( jqXHR ) ;
resultCheckStatusNovel ( ) ;
} ,
} ) ;
} 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-19 14:58:17 +02:00
$ ( '#hideCharPanelAvatarButton' ) . 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 setRightTabSelectedClass ( selectedButtonId ) {
document . querySelectorAll ( '#right-nav-panel-tabs .right_menu_button' ) . forEach ( ( button ) => {
button . classList . remove ( 'selected-right-tab' ) ;
if ( selectedButtonId && selectedButtonId . replace ( '#' , '' ) === button . id ) {
button . classList . add ( 'selected-right-tab' ) ;
}
} ) ;
}
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' ) ;
setTimeout ( function ( ) {
if ( type === 'char_import' || type === 'char_create' ) {
const element = $ ( ` #rm_characters_block [title=" ${ charId } "] ` ) . parent ( ) . get ( 0 ) ;
console . log ( element ) ;
element . scrollIntoView ( { behavior : 'smooth' , block : 'start' } ) ;
try {
if ( element !== undefined || element !== null ) {
$ ( element ) . addClass ( 'flash animated' ) ;
setTimeout ( function ( ) {
$ ( element ) . removeClass ( 'flash animated' ) ;
} , 5000 ) ;
} else { console . log ( 'didnt find the element' ) ; }
} catch ( e ) {
console . error ( e ) ;
}
}
if ( type === 'group_create' ) {
//for groups, ${charId} = data.id from group-chats.js createGroup()
const element = $ ( ` #rm_characters_block [grid=" ${ charId } "] ` ) . get ( 0 ) ;
element . scrollIntoView ( { behavior : 'smooth' , block : 'start' } ) ;
try {
if ( element !== undefined || element !== null ) {
$ ( element ) . addClass ( 'flash animated' ) ;
setTimeout ( function ( ) {
$ ( element ) . removeClass ( 'flash animated' ) ;
} , 5000 ) ;
} else { console . log ( 'didnt find the element' ) ; }
} catch ( e ) {
console . error ( e ) ;
}
}
} , 100 ) ;
setRightTabSelectedClass ( ) ;
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" ) ;
setRightTabSelectedClass ( 'rm_button_selected_ch' ) ;
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' ) ;
setRightTabSelectedClass ( ) ;
$ ( '#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 ( ) {
menu _type = "characters" ;
selectRightMenuWithAnimation ( 'rm_characters_block' ) ;
setRightTabSelectedClass ( 'rm_button_characters' ) ;
2023-08-19 01:53:05 +02:00
printCharacters ( false ) ; // Do a quick refresh of the characters list
2023-07-20 19:32:15 +02:00
}
function setExtensionPrompt ( key , value , position , depth ) {
extension _prompts [ key ] = { value , position , depth } ;
}
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 ) } ` ) ;
if ( swipeId !== undefined && swipeId != 0 ) {
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' ) ;
}
async function saveMetadata ( ) {
if ( selected _group ) {
2023-08-19 14:58:17 +02:00
await editGroup ( selected _group , false , false ) ;
2023-07-20 19:32:15 +02:00
}
else {
2023-08-19 14:58:17 +02:00
saveChatDebounced ( ) ;
2023-07-20 19:32:15 +02:00
}
}
export async function saveChatConditional ( ) {
if ( selected _group ) {
await saveGroupChat ( selected _group , true ) ;
}
else {
await saveChat ( ) ;
}
}
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 ( ) {
$ ( '#chat' ) . find ( ".mes" ) . each ( function ( index , element ) {
$ ( element ) . attr ( "mesid" , index ) ;
$ ( element ) . find ( '.mesIDDisplay' ) . text ( ` # ${ index } ` ) ;
} ) ;
$ ( '#chat .mes' ) . removeClass ( 'last_mes' ) ;
$ ( '#chat .mes' ) . last ( ) . addClass ( 'last_mes' ) ;
updateEditArrowClasses ( ) ;
}
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' , '' ) ;
saveChatConditional ( ) ;
}
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 ;
$ ( '#dialogue_popup' ) . addClass ( 'wide_dialogue_popup' ) ;
callPopup ( img . outerHTML , 'text' ) ;
}
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
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 ;
}
/ * l e t s e l e c t S c r o l l T o p = n u l l ;
if ( deviceInfo && deviceInfo . device . type === 'desktop' ) {
e . preventDefault ( ) ;
const option = $ ( e . target ) ;
const selectElement = $ ( extraSelect ) [ 0 ] ;
selectScrollTop = selectElement . scrollTop ;
option . prop ( 'selected' , ! option . prop ( 'selected' ) ) ;
await delay ( 1 ) ;
selectElement . scrollTop = selectScrollTop ;
} * /
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 {
$ ( "#result_info" ) . html ( "Name not entered" ) ;
}
} 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 ) {
if ( chat . length === 1 && ! selected _group ) {
var this _ch _mes = default _ch _mes ;
if ( $ ( "#firstmessage_textarea" ) . val ( ) != "" ) {
this _ch _mes = $ ( "#firstmessage_textarea" ) . val ( ) ;
}
if (
this _ch _mes !=
$ . trim (
$ ( "#chat" )
. children ( ".mes" )
. children ( ".mes_block" )
. children ( ".mes_text" )
. text ( )
)
) {
// MARK - kingbri: Regex on character greeting message
// May need to be placed somewhere else
this _ch _mes = getRegexedString ( this _ch _mes , regex _placement . AI _OUTPUT ) ;
clearChat ( ) ;
chat . length = 0 ;
chat [ 0 ] = { } ;
chat [ 0 ] [ "name" ] = name2 ;
chat [ 0 ] [ "is_user" ] = false ;
chat [ 0 ] [ "is_name" ] = true ;
chat [ 0 ] [ "mes" ] = this _ch _mes ;
chat [ 0 ] [ "extra" ] = { } ;
chat [ 0 ] [ "send_date" ] = getMessageTimeStamp ( ) ;
const alternateGreetings = characters [ this _chid ] ? . data ? . alternate _greetings ;
if ( Array . isArray ( alternateGreetings ) && alternateGreetings . length > 0 ) {
chat [ 0 ] [ 'swipe_id' ] = 0 ;
chat [ 0 ] [ 'swipes' ] = [ ] ;
chat [ 0 ] [ 'swipe_info' ] = [ ] ;
chat [ 0 ] [ 'swipes' ] [ 0 ] = chat [ 0 ] [ 'mes' ] ;
for ( let i = 0 ; i < alternateGreetings . length ; i ++ ) {
const alternateGreeting = getRegexedString ( alternateGreetings [ i ] , regex _placement . AI _OUTPUT ) ;
chat [ 0 ] [ 'swipes' ] . push ( substituteParams ( alternateGreeting ) ) ;
}
}
add _mes _without _animation = true ;
//console.log('form create submission calling addOneMessage');
addOneMessage ( chat [ 0 ] ) ;
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
}
}
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
2023-08-19 14:58:17 +02:00
await getOneCharacter ( formData . get ( 'avatar_url' ) ) ;
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-07-20 19:32:15 +02:00
} ,
error : function ( jqXHR , exception ) {
$ ( "#create_button" ) . removeAttr ( "disabled" ) ;
$ ( "#result_info" ) . html ( "<font color=red>Error: no connection</font>" ) ;
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 ,
} ;
} ;
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' ] -- ;
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' } ) ;
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 ) ) ;
await saveChatConditional ( ) ;
}
} ) ;
}
} ) ;
}
} ) ;
$ ( 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 ;
}
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 ;
}
}
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
}
chat [ chat . length - 1 ] [ 'swipe_id' ] ++ ; //make new slot in array
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']);
if ( parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) === chat [ chat . length - 1 ] [ 'swipes' ] . length ) { //if swipe id of last message is the same as the length of the 'swipes' array
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);
if ( run _generate && parseInt ( chat [ chat . length - 1 ] [ 'swipe_id' ] ) === chat [ chat . length - 1 ] [ 'swipes' ] . length ) {
//console.log('showing ""..."');
/ * i f ( ! s e l e c t e d _ g r o u p ) {
} else { * /
$ ( "#chat" )
. find ( '[mesid="' + ( count _view _mes - 1 ) + '"]' )
. find ( '.mes_text' )
. html ( '...' ) ; //shows "..." while generating
$ ( "#chat" )
. find ( '[mesid="' + ( count _view _mes - 1 ) + '"]' )
. find ( '.mes_timer' )
. html ( '' ) ; // resets the timer
/* } */
} else {
//console.log('showing previously generated swipe candidate, or "..."');
//console.log('onclick right swipe calling addOneMessage');
addOneMessage ( chat [ chat . length - 1 ] , { type : 'swipe' } ) ;
}
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 ) {
await saveChatConditional ( ) ;
}
}
}
} ) ;
}
} ) ;
}
} ) ;
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.. ` ) ;
}
// Check for override warnings every 5 seconds...
setInterval ( displayOverrideWarnings , 5000 ) ;
// ...or when the chat changes
eventSource . on ( event _types . CHAT _CHANGED , displayOverrideWarnings ) ;
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 ;
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 ) ;
} 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 = { } ;
setRightTabSelectedClass ( ) ;
$ ( 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 ) ;
printMessages ( ) ;
saveSettingsDebounced ( ) ;
}
2023-08-03 06:21:38 +02:00
function doTogglePanels ( ) {
$ ( "#option_settings" ) . trigger ( 'click' )
}
2023-07-24 21:05:27 +02:00
2023-07-20 19:32:15 +02:00
$ ( document ) . ready ( function ( ) {
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 ) ;
$ ( '#options_button, #send_but, #option_regenerate' ) . on ( 'click' , ( ) => {
if ( S _TAPreviouslyFocused ) {
$ ( '#send_textarea' ) . focus ( ) ;
S _TAFocused = true ;
}
} ) ;
$ ( document ) . click ( event => {
if ( $ ( ':focus' ) . attr ( 'id' ) !== 'send_textarea' ) {
var validIDs = [ "options_button" , "send_but" , "send_textarea" , "option_regenerate" ] ;
if ( $ ( event . target ) . attr ( 'id' ) !== validIDs ) {
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-18 22:13:15 +02:00
const searchValue = $ ( this ) . val ( ) . toLowerCase ( ) ;
entitiesFilter . setFilterData ( FILTER _TYPES . SEARCH , searchValue ) ;
2023-07-20 19:32:15 +02:00
} ) ;
$ ( "#send_but" ) . click ( function ( ) {
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' ) ;
setRightTabSelectedClass ( 'rm_button_settings' ) ;
} ) ;
$ ( "#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
var i = $ ( this ) . attr ( "mesid" ) ; //checks the message ID in the chat
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" ) ;
}
} ) ;
$ ( "#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 ) ;
} ) ;
$ ( "#character_popup_ok" ) . click ( function ( ) {
is _advanced _char _open = false ;
$ ( "#character_popup" ) . css ( "display" , "none" ) ;
} ) ;
$ ( "#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 ) {
createNewGroupChat ( selected _group ) ;
}
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 ) ;
getChat ( ) ;
}
}
rawPromptPopper . update ( ) ;
$ ( '#rawPromptPopup' ) . hide ( ) ;
if ( dialogueResolve ) {
if ( popup _type == 'input' ) {
dialogueResolve ( $ ( "#dialogue_popup_input" ) . val ( ) ) ;
$ ( "#dialogue_popup_input" ) . val ( '' ) ;
}
else {
dialogueResolve ( true ) ;
}
dialogueResolve = null ;
}
} ) ;
$ ( "#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 ;
saveCharacterDebounced ( ) ;
}
}
reloadCurrentChat ( ) ;
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 ) {
e . stopPropagation ( ) ;
if ( $ ( "#api_url_text" ) . val ( ) != "" ) {
let value = formatKoboldUrl ( $ . trim ( $ ( "#api_url_text" ) . val ( ) ) ) ;
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-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-08-03 12:07:54 +02:00
$ ( "#api_button_textgenerationwebui" ) . click ( async function ( e ) {
2023-07-20 19:32:15 +02:00
e . stopPropagation ( ) ;
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 ( ) != "" ) {
let value = formatTextGenURL ( $ ( 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-03 05:25:24 +02:00
const mancer _key = $ ( "#api_key_mancer" ) . val ( ) . trim ( ) ;
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-07-20 19:32:15 +02:00
$ ( "#dialogue_del_mes_ok" ) . 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 ) ;
} ) ;
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 ;
saveChatConditional ( ) ;
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 ) ;
$ ( "#kobold-advanced-config" ) . find ( 'input' ) . prop ( "disabled" , true ) ;
$ ( "#kobold-advanced-config" ) . css ( 'opacity' , 0.5 ) ;
$ ( "#range_block" ) . css ( "opacity" , 0.5 ) ;
$ ( "#amount_gen_block" ) . find ( 'input' ) . prop ( "disabled" , true ) ;
$ ( "#amount_gen_block" ) . css ( "opacity" , 0.45 ) ;
$ ( "#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 ) ;
amount _gen = parseInt ( $ ( "#amount_gen" ) . val ( ) ) ;
max _context = parseInt ( $ ( "#max_context" ) . val ( ) ) ;
saveSettingsDebounced ( ) ;
} ) ;
$ ( "#main_api" ) . change ( function ( ) {
is _pygmalion = false ;
is _get _status = false ;
is _get _status _novel = false ;
setOpenAIOnlineStatus ( false ) ;
online _status = "no_connection" ;
checkOnlineStatus ( ) ;
changeMainAPI ( ) ;
saveSettingsDebounced ( ) ;
} ) ;
////////////////// OPTIMIZED RANGE SLIDER LISTENERS////////////////
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 ) ;
}
} )
$ ( 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 ;
} ) ;
$ ( document ) . on ( "click" , ".mes_edit_up" , function ( ) {
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 ( ) ;
saveChatConditional ( ) ;
showSwipeButtons ( ) ;
} ) ;
$ ( document ) . on ( "click" , ".mes_edit_down" , function ( ) {
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 ( ) ;
saveChatConditional ( ) ;
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 ( ) ;
saveChatConditional ( ) ;
$ ( '#chat' ) [ 0 ] . scrollTop = oldScroll ;
showSwipeButtons ( ) ;
} ) ;
$ ( document ) . on ( "click" , ".mes_edit_delete" , async function ( event , customData ) {
const fromSlashCommand = customData ? . fromSlashCommand || false ;
if ( power _user . confirm _message _delete && fromSlashCommand !== true ) {
const confirmation = await callPopup ( "Are you sure you want to delete this message?" , 'confirm' ) ;
if ( ! confirmation ) {
return ;
}
}
const mes = $ ( this ) . closest ( ".mes" ) ;
if ( ! mes ) {
return ;
}
chat . splice ( this _edit _mes _id , 1 ) ;
this _edit _mes _id = undefined ;
mes . remove ( ) ;
count _view _mes -- ;
updateViewMessageIds ( ) ;
saveChatConditional ( ) ;
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 ( ) ) ;
} ) ;
$ ( "#create_dummy_persona" ) . on ( 'click' , createDummyPersona ) ;
$ ( '#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 ( ) ;
const api _key _novel = $ ( "#api_key_novel" ) . val ( ) . trim ( ) ;
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 ) ;
} ) ;
$ ( document ) . on ( 'click' , '.bind_user_name' , bindUserNameToPersona ) ;
$ ( document ) . on ( 'click' , '.delete_avatar' , deleteUserAvatar ) ;
$ ( document ) . on ( 'click' , '.set_default_persona' , setDefaultPersona ) ;
$ ( '#lock_user_name' ) . on ( 'click' , lockUserNameToChat ) ;
$ ( '#persona_description' ) . on ( 'input' , onPersonaDescriptionInput ) ;
$ ( '#persona_description_position' ) . on ( 'input' , onPersonaDescriptionPositionInput ) ;
//**************************CHARACTER IMPORT EXPORT*************************//
$ ( "#character_import_button" ) . click ( function ( ) {
$ ( "#character_import_file" ) . click ( ) ;
} ) ;
$ ( "#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 ) ;
}
} ) ;
$ ( "#export_button" ) . on ( 'click' , function ( e ) {
$ ( '#export_format_popup' ) . toggle ( ) ;
exportPopper . update ( ) ;
} ) ;
$ ( 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' ) ;
} ) ;
}
} 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' ,
] ;
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 ( ) ;
} ) ;
$ ( 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' , '' ) ;
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 ( ) {
const filterValue = $ ( this ) . val ( ) . toLowerCase ( ) ;
$ ( "#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 ; * /
}
$ ( "#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 :
toastr . warn ( 'Unknown content type' ) ;
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 ) ;
} ) ;
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 ) ;
}
}
}
$ ( "#charListGridToggle" ) . on ( 'click' , async ( ) => {
doCharListDisplaySwitch ( ) ;
} ) ;
2023-08-18 15:45:40 +02:00
2023-08-19 14:58:17 +02:00
$ ( "#hideCharPanelAvatarButton" ) . hide ( ) . on ( 'click' , ( ) => {
2023-08-18 15:45:40 +02:00
$ ( '#avatar-and-name-block' ) . slideToggle ( )
2023-08-19 14:58:17 +02:00
} ) ;
2023-07-20 19:32:15 +02:00
} ) ;