2023-07-20 19:32:15 +02:00
import {
chat _metadata ,
eventSource ,
event _types ,
saveSettingsDebounced ,
this _chid ,
} from "../script.js" ;
import { selected _group } from "./group-chats.js" ;
import { extension _settings , getContext , saveMetadataDebounced } from "./extensions.js" ;
import { registerSlashCommand } from "./slash-commands.js" ;
import { getCharaFilename , debounce , waitUntilCondition , delay } from "./utils.js" ;
2023-08-23 01:38:43 +02:00
import { getTokenCount } from "./tokenizers.js" ;
2023-07-20 19:32:15 +02:00
export { MODULE _NAME as NOTE _MODULE _NAME } ;
const MODULE _NAME = '2_floating_prompt' ; // <= Deliberate, for sorting lower than memory
export var shouldWIAddPrompt = false ;
export const metadata _keys = {
prompt : 'note_prompt' ,
interval : 'note_interval' ,
depth : 'note_depth' ,
position : 'note_position' ,
}
const chara _note _position = {
replace : 0 ,
before : 1 ,
after : 2 ,
}
function setNoteTextCommand ( _ , text ) {
$ ( '#extension_floating_prompt' ) . val ( text ) . trigger ( 'input' ) ;
toastr . success ( "Author's Note text updated" ) ;
}
function setNoteDepthCommand ( _ , text ) {
const value = Number ( text ) ;
if ( Number . isNaN ( value ) ) {
toastr . error ( 'Not a valid number' ) ;
return ;
}
$ ( '#extension_floating_depth' ) . val ( Math . abs ( value ) ) . trigger ( 'input' ) ;
toastr . success ( "Author's Note depth updated" ) ;
}
function setNoteIntervalCommand ( _ , text ) {
const value = Number ( text ) ;
if ( Number . isNaN ( value ) ) {
toastr . error ( 'Not a valid number' ) ;
return ;
}
$ ( '#extension_floating_interval' ) . val ( Math . abs ( value ) ) . trigger ( 'input' ) ;
toastr . success ( "Author's Note frequency updated" ) ;
}
function setNotePositionCommand ( _ , text ) {
const validPositions = {
'scenario' : 0 ,
'chat' : 1 ,
} ;
const position = validPositions [ text ? . trim ( ) ] ;
if ( Number . isNaN ( position ) ) {
toastr . error ( 'Not a valid position' ) ;
return ;
}
$ ( ` input[name="extension_floating_position"][value=" ${ position } "] ` ) . prop ( 'checked' , true ) . trigger ( 'input' ) ;
toastr . info ( "Author's Note position updated" ) ;
}
function updateSettings ( ) {
saveSettingsDebounced ( ) ;
loadSettings ( ) ;
setFloatingPrompt ( ) ;
}
const setMainPromptTokenCounterDebounced = debounce ( ( value ) => $ ( '#extension_floating_prompt_token_counter' ) . text ( getTokenCount ( value ) ) , 1000 ) ;
const setCharaPromptTokenCounterDebounced = debounce ( ( value ) => $ ( '#extension_floating_chara_token_counter' ) . text ( getTokenCount ( value ) ) , 1000 ) ;
const setDefaultPromptTokenCounterDebounced = debounce ( ( value ) => $ ( '#extension_floating_default_token_counter' ) . text ( getTokenCount ( value ) ) , 1000 ) ;
async function onExtensionFloatingPromptInput ( ) {
chat _metadata [ metadata _keys . prompt ] = $ ( this ) . val ( ) ;
setMainPromptTokenCounterDebounced ( chat _metadata [ metadata _keys . prompt ] ) ;
updateSettings ( ) ;
saveMetadataDebounced ( ) ;
}
async function onExtensionFloatingIntervalInput ( ) {
chat _metadata [ metadata _keys . interval ] = Number ( $ ( this ) . val ( ) ) ;
updateSettings ( ) ;
saveMetadataDebounced ( ) ;
}
async function onExtensionFloatingDepthInput ( ) {
let value = Number ( $ ( this ) . val ( ) ) ;
if ( value < 0 ) {
value = Math . abs ( value ) ;
$ ( this ) . val ( value ) ;
}
chat _metadata [ metadata _keys . depth ] = value ;
updateSettings ( ) ;
saveMetadataDebounced ( ) ;
}
async function onExtensionFloatingPositionInput ( e ) {
chat _metadata [ metadata _keys . position ] = e . target . value ;
updateSettings ( ) ;
saveMetadataDebounced ( ) ;
}
2023-08-11 16:40:02 +02:00
async function onDefaultPositionInput ( e ) {
extension _settings . note . defaultPosition = e . target . value ;
saveSettingsDebounced ( ) ;
}
async function onDefaultDepthInput ( ) {
let value = Number ( $ ( this ) . val ( ) ) ;
if ( value < 0 ) {
value = Math . abs ( value ) ;
$ ( this ) . val ( value ) ;
}
extension _settings . note . defaultDepth = value ;
saveSettingsDebounced ( ) ;
}
async function onDefaultIntervalInput ( ) {
extension _settings . note . defaultInterval = Number ( $ ( this ) . val ( ) ) ;
saveSettingsDebounced ( ) ;
}
2023-07-20 19:32:15 +02:00
async function onExtensionFloatingCharPositionInput ( e ) {
const value = e . target . value ;
const charaNote = extension _settings . note . chara . find ( ( e ) => e . name === getCharaFilename ( ) ) ;
if ( charaNote ) {
charaNote . position = Number ( value ) ;
updateSettings ( ) ;
}
}
function onExtensionFloatingCharaPromptInput ( ) {
const tempPrompt = $ ( this ) . val ( ) ;
const avatarName = getCharaFilename ( ) ;
let tempCharaNote = {
name : avatarName ,
prompt : tempPrompt
}
setCharaPromptTokenCounterDebounced ( tempPrompt ) ;
let existingCharaNoteIndex ;
let existingCharaNote ;
if ( extension _settings . note . chara ) {
existingCharaNoteIndex = extension _settings . note . chara . findIndex ( ( e ) => e . name === avatarName ) ;
existingCharaNote = extension _settings . note . chara [ existingCharaNoteIndex ]
}
if ( tempPrompt . length === 0 &&
extension _settings . note . chara &&
existingCharaNote &&
! existingCharaNote . useChara
) {
extension _settings . note . chara . splice ( existingCharaNoteIndex , 1 ) ;
}
else if ( extension _settings . note . chara && existingCharaNote ) {
Object . assign ( existingCharaNote , tempCharaNote ) ;
}
else if ( avatarName && tempPrompt . length > 0 ) {
if ( ! extension _settings . note . chara ) {
extension _settings . note . chara = [ ]
}
Object . assign ( tempCharaNote , { useChara : false , position : chara _note _position . replace } )
extension _settings . note . chara . push ( tempCharaNote ) ;
} else {
console . log ( "Character author's note error: No avatar name key could be found." ) ;
toastr . error ( "Something went wrong. Could not save character's author's note." ) ;
// Don't save settings if something went wrong
return ;
}
updateSettings ( ) ;
}
function onExtensionFloatingCharaCheckboxChanged ( ) {
const value = ! ! $ ( this ) . prop ( 'checked' ) ;
const charaNote = extension _settings . note . chara . find ( ( e ) => e . name === getCharaFilename ( ) ) ;
if ( charaNote ) {
charaNote . useChara = value ;
updateSettings ( ) ;
}
}
function onExtensionFloatingDefaultInput ( ) {
extension _settings . note . default = $ ( this ) . val ( ) ;
setDefaultPromptTokenCounterDebounced ( extension _settings . note . default ) ;
updateSettings ( ) ;
}
function loadSettings ( ) {
2023-08-11 16:40:02 +02:00
const DEFAULT _DEPTH = 4 ;
const DEFAULT _POSITION = 1 ;
const DEFAULT _INTERVAL = 1 ;
if ( extension _settings . note . defaultPosition === undefined ) {
extension _settings . note . defaultPosition = DEFAULT _POSITION ;
}
if ( extension _settings . note . defaultDepth === undefined ) {
extension _settings . note . defaultDepth = DEFAULT _DEPTH ;
}
if ( extension _settings . note . defaultInterval === undefined ) {
extension _settings . note . defaultInterval = DEFAULT _INTERVAL ;
}
2023-07-20 19:32:15 +02:00
chat _metadata [ metadata _keys . prompt ] = chat _metadata [ metadata _keys . prompt ] ? ? extension _settings . note . default ? ? '' ;
2023-08-11 16:40:02 +02:00
chat _metadata [ metadata _keys . interval ] = chat _metadata [ metadata _keys . interval ] ? ? extension _settings . note . defaultInterval ? ? DEFAULT _INTERVAL ;
chat _metadata [ metadata _keys . position ] = chat _metadata [ metadata _keys . position ] ? ? extension _settings . note . defaultPosition ? ? DEFAULT _POSITION ;
chat _metadata [ metadata _keys . depth ] = chat _metadata [ metadata _keys . depth ] ? ? extension _settings . note . defaultDepth ? ? DEFAULT _DEPTH ;
2023-07-20 19:32:15 +02:00
$ ( '#extension_floating_prompt' ) . val ( chat _metadata [ metadata _keys . prompt ] ) ;
$ ( '#extension_floating_interval' ) . val ( chat _metadata [ metadata _keys . interval ] ) ;
2023-10-12 05:44:52 +02:00
$ ( '#extension_floating_allow_wi_scan' ) . prop ( 'checked' , extension _settings . note . allowWIScan ? ? false ) ;
2023-07-20 19:32:15 +02:00
$ ( '#extension_floating_depth' ) . val ( chat _metadata [ metadata _keys . depth ] ) ;
$ ( ` input[name="extension_floating_position"][value=" ${ chat _metadata [ metadata _keys . position ] } "] ` ) . prop ( 'checked' , true ) ;
if ( extension _settings . note . chara && getContext ( ) . characterId ) {
const charaNote = extension _settings . note . chara . find ( ( e ) => e . name === getCharaFilename ( ) ) ;
$ ( '#extension_floating_chara' ) . val ( charaNote ? charaNote . prompt : '' ) ;
$ ( '#extension_use_floating_chara' ) . prop ( 'checked' , charaNote ? charaNote . useChara : false ) ;
$ ( ` input[name="extension_floating_char_position"][value=" ${ charaNote ? . position ? ? chara _note _position . replace } "] ` ) . prop ( 'checked' , true ) ;
} else {
$ ( '#extension_floating_chara' ) . val ( '' ) ;
$ ( '#extension_use_floating_chara' ) . prop ( 'checked' , false ) ;
$ ( ` input[name="extension_floating_char_position"][value=" ${ chara _note _position . replace } "] ` ) . prop ( 'checked' , true ) ;
}
$ ( '#extension_floating_default' ) . val ( extension _settings . note . default ) ;
2023-08-11 16:40:02 +02:00
$ ( '#extension_default_depth' ) . val ( extension _settings . note . defaultDepth ) ;
$ ( '#extension_default_interval' ) . val ( extension _settings . note . defaultInterval ) ;
$ ( ` input[name="extension_default_position"][value=" ${ extension _settings . note . defaultPosition } "] ` ) . prop ( 'checked' , true ) ;
2023-07-20 19:32:15 +02:00
}
export function setFloatingPrompt ( ) {
const context = getContext ( ) ;
if ( ! context . groupId && context . characterId === undefined ) {
console . debug ( 'setFloatingPrompt: Not in a chat. Skipping.' ) ;
shouldWIAddPrompt = false ;
return ;
}
// take the count of messages
let lastMessageNumber = Array . isArray ( context . chat ) && context . chat . length ? context . chat . filter ( m => m . is _user ) . length : 0 ;
console . debug ( `
setFloatingPrompt entered
-- -- --
lastMessageNumber = $ { lastMessageNumber }
metadata _keys . interval = $ { chat _metadata [ metadata _keys . interval ] }
` )
// interval 1 should be inserted no matter what
if ( chat _metadata [ metadata _keys . interval ] === 1 ) {
lastMessageNumber = 1 ;
}
if ( lastMessageNumber <= 0 || chat _metadata [ metadata _keys . interval ] <= 0 ) {
context . setExtensionPrompt ( MODULE _NAME , '' ) ;
$ ( '#extension_floating_counter' ) . text ( '(disabled)' ) ;
shouldWIAddPrompt = false ;
return ;
}
const messagesTillInsertion = lastMessageNumber >= chat _metadata [ metadata _keys . interval ]
? ( lastMessageNumber % chat _metadata [ metadata _keys . interval ] )
: ( chat _metadata [ metadata _keys . interval ] - lastMessageNumber ) ;
const shouldAddPrompt = messagesTillInsertion == 0 ;
shouldWIAddPrompt = shouldAddPrompt ;
let prompt = shouldAddPrompt ? $ ( '#extension_floating_prompt' ) . val ( ) : '' ;
if ( shouldAddPrompt && extension _settings . note . chara && getContext ( ) . characterId ) {
const charaNote = extension _settings . note . chara . find ( ( e ) => e . name === getCharaFilename ( ) ) ;
// Only replace with the chara note if the user checked the box
if ( charaNote && charaNote . useChara ) {
switch ( charaNote . position ) {
case chara _note _position . before :
prompt = charaNote . prompt + '\n' + prompt ;
break ;
case chara _note _position . after :
prompt = prompt + '\n' + charaNote . prompt ;
break ;
default :
prompt = charaNote . prompt ;
break ;
}
}
}
context . setExtensionPrompt ( MODULE _NAME , prompt , chat _metadata [ metadata _keys . position ] , chat _metadata [ metadata _keys . depth ] ) ;
$ ( '#extension_floating_counter' ) . text ( shouldAddPrompt ? '0' : messagesTillInsertion ) ;
}
function onANMenuItemClick ( ) {
if ( selected _group || this _chid ) {
//show AN if it's hidden
if ( $ ( "#floatingPrompt" ) . css ( "display" ) !== 'flex' ) {
$ ( "#floatingPrompt" ) . addClass ( 'resizing' )
$ ( "#floatingPrompt" ) . css ( "display" , "flex" ) ;
$ ( "#floatingPrompt" ) . css ( "opacity" , 0.0 ) ;
$ ( "#floatingPrompt" ) . transition ( {
opacity : 1.0 ,
duration : 250 ,
} , async function ( ) {
await delay ( 50 ) ;
$ ( "#floatingPrompt" ) . removeClass ( 'resizing' )
} ) ;
//auto-open the main AN inline drawer
if ( $ ( "#ANBlockToggle" )
. siblings ( '.inline-drawer-content' )
. css ( 'display' ) !== 'block' ) {
$ ( "#floatingPrompt" ) . addClass ( 'resizing' )
$ ( "#ANBlockToggle" ) . click ( ) ;
}
} else {
//hide AN if it's already displayed
$ ( "#floatingPrompt" ) . addClass ( 'resizing' )
$ ( "#floatingPrompt" ) . transition ( {
opacity : 0.0 ,
duration : 250 ,
} ,
async function ( ) {
await delay ( 50 ) ;
$ ( "#floatingPrompt" ) . removeClass ( 'resizing' )
} ) ;
setTimeout ( function ( ) {
$ ( "#floatingPrompt" ) . hide ( ) ;
} , 250 ) ;
}
//duplicate options menu close handler from script.js
//because this listener takes priority
$ ( "#options" ) . stop ( ) . fadeOut ( 250 ) ;
} else {
toastr . warning ( ` Select a character before trying to use Author's Note ` , '' , { timeOut : 2000 } ) ;
}
}
function onChatChanged ( ) {
loadSettings ( ) ;
setFloatingPrompt ( ) ;
const context = getContext ( ) ;
// Disable the chara note if in a group
$ ( '#extension_floating_chara' ) . prop ( 'disabled' , context . groupId ? true : false ) ;
const tokenCounter1 = chat _metadata [ metadata _keys . prompt ] ? getTokenCount ( chat _metadata [ metadata _keys . prompt ] ) : 0 ;
$ ( '#extension_floating_prompt_token_counter' ) . text ( tokenCounter1 ) ;
let tokenCounter2 ;
if ( extension _settings . note . chara && context . characterId ) {
const charaNote = extension _settings . note . chara . find ( ( e ) => e . name === getCharaFilename ( ) ) ;
if ( charaNote ) {
tokenCounter2 = getTokenCount ( charaNote . prompt ) ;
}
}
if ( tokenCounter2 ) {
$ ( '#extension_floating_chara_token_counter' ) . text ( tokenCounter2 ) ;
}
const tokenCounter3 = extension _settings . note . default ? getTokenCount ( extension _settings . note . default ) : 0 ;
$ ( '#extension_floating_default_token_counter' ) . text ( tokenCounter3 ) ;
}
2023-10-12 05:44:52 +02:00
function onAllowWIScanCheckboxChanged ( ) {
extension _settings . note . allowWIScan = ! ! $ ( this ) . prop ( 'checked' ) ;
updateSettings ( ) ;
}
2023-08-25 15:46:54 +02:00
/ * *
* Inject author ' s note options and setup event listeners .
* /
2023-07-20 19:32:15 +02:00
// Inserts the extension first since it's statically imported
2023-08-25 15:46:54 +02:00
export function initAuthorsNote ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#extension_floating_prompt' ) . on ( 'input' , onExtensionFloatingPromptInput ) ;
$ ( '#extension_floating_interval' ) . on ( 'input' , onExtensionFloatingIntervalInput ) ;
$ ( '#extension_floating_depth' ) . on ( 'input' , onExtensionFloatingDepthInput ) ;
$ ( '#extension_floating_chara' ) . on ( 'input' , onExtensionFloatingCharaPromptInput ) ;
$ ( '#extension_use_floating_chara' ) . on ( 'input' , onExtensionFloatingCharaCheckboxChanged ) ;
$ ( '#extension_floating_default' ) . on ( 'input' , onExtensionFloatingDefaultInput ) ;
2023-08-11 16:40:02 +02:00
$ ( '#extension_default_depth' ) . on ( 'input' , onDefaultDepthInput ) ;
$ ( '#extension_default_interval' ) . on ( 'input' , onDefaultIntervalInput ) ;
2023-10-12 05:44:52 +02:00
$ ( '#extension_floating_allow_wi_scan' ) . on ( 'input' , onAllowWIScanCheckboxChanged ) ;
2023-07-20 19:32:15 +02:00
$ ( 'input[name="extension_floating_position"]' ) . on ( 'change' , onExtensionFloatingPositionInput ) ;
2023-08-11 16:40:02 +02:00
$ ( 'input[name="extension_default_position"]' ) . on ( 'change' , onDefaultPositionInput ) ;
2023-07-20 19:32:15 +02:00
$ ( 'input[name="extension_floating_char_position"]' ) . on ( 'change' , onExtensionFloatingCharPositionInput ) ;
$ ( '#ANClose' ) . on ( 'click' , function ( ) {
$ ( "#floatingPrompt" ) . transition ( {
opacity : 0 ,
duration : 200 ,
easing : 'ease-in-out' ,
} ) ;
setTimeout ( function ( ) { $ ( '#floatingPrompt' ) . hide ( ) } , 200 ) ;
} )
$ ( "#option_toggle_AN" ) . on ( 'click' , onANMenuItemClick ) ;
registerSlashCommand ( 'note' , setNoteTextCommand , [ ] , "<span class='monospace'>(text)</span> – sets an author's note for the currently selected chat" , true , true ) ;
registerSlashCommand ( 'depth' , setNoteDepthCommand , [ ] , "<span class='monospace'>(number)</span> – sets an author's note depth for in-chat positioning" , true , true ) ;
registerSlashCommand ( 'freq' , setNoteIntervalCommand , [ 'interval' ] , "<span class='monospace'>(number)</span> – sets an author's note insertion frequency" , true , true ) ;
registerSlashCommand ( 'pos' , setNotePositionCommand , [ 'position' ] , "(<span class='monospace'>chat</span> or <span class='monospace'>scenario</span>) – sets an author's note position" , true , true ) ;
eventSource . on ( event _types . CHAT _CHANGED , onChatChanged ) ;
2023-08-25 15:46:54 +02:00
}