2023-07-20 19:32:15 +02:00
import {
2023-12-10 19:02:25 +01:00
animation _duration ,
2023-07-20 19:32:15 +02:00
chat _metadata ,
eventSource ,
event _types ,
2024-03-23 17:45:37 +01:00
extension _prompt _roles ,
2023-07-20 19:32:15 +02:00
saveSettingsDebounced ,
this _chid ,
2023-12-02 19:04:51 +01:00
} 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 , delay } from './utils.js' ;
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' ,
2024-03-23 17:45:37 +01:00
role : 'note_role' ,
2023-12-02 20:11:06 +01:00
} ;
2023-07-20 19:32:15 +02:00
const chara _note _position = {
replace : 0 ,
before : 1 ,
after : 2 ,
2023-12-02 20:11:06 +01:00
} ;
2023-07-20 19:32:15 +02:00
function setNoteTextCommand ( _ , text ) {
$ ( '#extension_floating_prompt' ) . val ( text ) . trigger ( 'input' ) ;
2023-12-02 19:04:51 +01:00
toastr . success ( 'Author\'s Note text updated' ) ;
2023-07-20 19:32:15 +02:00
}
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' ) ;
2023-12-02 19:04:51 +01:00
toastr . success ( 'Author\'s Note depth updated' ) ;
2023-07-20 19:32:15 +02:00
}
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' ) ;
2023-12-02 19:04:51 +01:00
toastr . success ( 'Author\'s Note frequency updated' ) ;
2023-07-20 19:32:15 +02:00
}
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' ) ;
2023-12-02 19:04:51 +01:00
toastr . info ( 'Author\'s Note position updated' ) ;
2023-07-20 19:32:15 +02:00
}
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 ) {
2024-03-24 02:12:30 +01:00
chat _metadata [ metadata _keys . position ] = Number ( e . target . value ) ;
2023-07-20 19:32:15 +02:00
updateSettings ( ) ;
saveMetadataDebounced ( ) ;
}
2023-08-11 16:40:02 +02:00
async function onDefaultPositionInput ( e ) {
2024-03-24 02:12:30 +01:00
extension _settings . note . defaultPosition = Number ( e . target . value ) ;
2023-08-11 16:40:02 +02:00
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 ( ) ;
}
2024-03-23 17:45:37 +01:00
function onExtensionFloatingRoleInput ( e ) {
chat _metadata [ metadata _keys . role ] = Number ( e . target . value ) ;
updateSettings ( ) ;
}
function onExtensionDefaultRoleInput ( e ) {
extension _settings . note . defaultRole = Number ( e . target . value ) ;
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 ,
2023-12-02 21:06:57 +01:00
prompt : tempPrompt ,
2023-12-02 20:11:06 +01:00
} ;
2023-07-20 19:32:15 +02:00
setCharaPromptTokenCounterDebounced ( tempPrompt ) ;
let existingCharaNoteIndex ;
let existingCharaNote ;
if ( extension _settings . note . chara ) {
existingCharaNoteIndex = extension _settings . note . chara . findIndex ( ( e ) => e . name === avatarName ) ;
2023-12-02 20:11:06 +01:00
existingCharaNote = extension _settings . note . chara [ existingCharaNoteIndex ] ;
2023-07-20 19:32:15 +02:00
}
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 ) {
2023-12-02 20:11:06 +01:00
extension _settings . note . chara = [ ] ;
2023-07-20 19:32:15 +02:00
}
2023-12-02 20:11:06 +01:00
Object . assign ( tempCharaNote , { useChara : false , position : chara _note _position . replace } ) ;
2023-07-20 19:32:15 +02:00
extension _settings . note . chara . push ( tempCharaNote ) ;
} else {
2023-12-02 19:04:51 +01:00
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.' ) ;
2023-07-20 19:32:15 +02:00
// 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 ;
2024-03-23 17:45:37 +01:00
const DEFAULT _ROLE = extension _prompt _roles . SYSTEM ;
2023-08-11 16:40:02 +02:00
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 ;
}
2024-03-23 17:45:37 +01:00
if ( extension _settings . note . defaultRole === undefined ) {
extension _settings . note . defaultRole = DEFAULT _ROLE ;
}
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 ;
2024-03-23 17:45:37 +01:00
chat _metadata [ metadata _keys . role ] = chat _metadata [ metadata _keys . role ] ? ? extension _settings . note . defaultRole ? ? DEFAULT _ROLE ;
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 ] ) ;
2024-03-23 17:45:37 +01:00
$ ( '#extension_floating_role' ) . val ( chat _metadata [ metadata _keys . role ] ) ;
2023-07-20 19:32:15 +02:00
$ ( ` 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 ) ;
2024-03-23 17:45:37 +01:00
$ ( '#extension_default_role' ) . val ( extension _settings . note . defaultRole ) ;
2023-08-11 16:40:02 +02:00
$ ( ` 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 ] }
2024-03-23 17:45:37 +01:00
metadata _keys . position = $ { chat _metadata [ metadata _keys . position ] }
metadata _keys . depth = $ { chat _metadata [ metadata _keys . depth ] }
metadata _keys . role = $ { chat _metadata [ metadata _keys . role ] }
-- -- --
2023-12-02 20:11:06 +01:00
` );
2023-07-20 19:32:15 +02:00
// 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 ;
}
}
}
2024-03-23 17:45:37 +01:00
context . setExtensionPrompt (
MODULE _NAME ,
prompt ,
chat _metadata [ metadata _keys . position ] ,
chat _metadata [ metadata _keys . depth ] ,
extension _settings . note . allowWIScan ,
chat _metadata [ metadata _keys . role ] ,
) ;
2023-07-20 19:32:15 +02:00
$ ( '#extension_floating_counter' ) . text ( shouldAddPrompt ? '0' : messagesTillInsertion ) ;
}
function onANMenuItemClick ( ) {
if ( selected _group || this _chid ) {
//show AN if it's hidden
2023-12-02 19:04:51 +01:00
if ( $ ( '#floatingPrompt' ) . css ( 'display' ) !== 'flex' ) {
2023-12-02 20:11:06 +01:00
$ ( '#floatingPrompt' ) . addClass ( 'resizing' ) ;
2023-12-02 19:04:51 +01:00
$ ( '#floatingPrompt' ) . css ( 'display' , 'flex' ) ;
$ ( '#floatingPrompt' ) . css ( 'opacity' , 0.0 ) ;
$ ( '#floatingPrompt' ) . transition ( {
2023-07-20 19:32:15 +02:00
opacity : 1.0 ,
2023-12-10 19:02:25 +01:00
duration : animation _duration ,
2023-07-20 19:32:15 +02:00
} , async function ( ) {
await delay ( 50 ) ;
2023-12-02 20:11:06 +01:00
$ ( '#floatingPrompt' ) . removeClass ( 'resizing' ) ;
2023-07-20 19:32:15 +02:00
} ) ;
//auto-open the main AN inline drawer
2023-12-02 19:04:51 +01:00
if ( $ ( '#ANBlockToggle' )
2023-07-20 19:32:15 +02:00
. siblings ( '.inline-drawer-content' )
. css ( 'display' ) !== 'block' ) {
2023-12-02 20:11:06 +01:00
$ ( '#floatingPrompt' ) . addClass ( 'resizing' ) ;
2023-12-02 19:04:51 +01:00
$ ( '#ANBlockToggle' ) . click ( ) ;
2023-07-20 19:32:15 +02:00
}
} else {
//hide AN if it's already displayed
2023-12-02 20:11:06 +01:00
$ ( '#floatingPrompt' ) . addClass ( 'resizing' ) ;
2023-12-02 19:04:51 +01:00
$ ( '#floatingPrompt' ) . transition ( {
2023-07-20 19:32:15 +02:00
opacity : 0.0 ,
2023-12-10 19:02:25 +01:00
duration : animation _duration ,
2023-07-20 19:32:15 +02:00
} ,
2023-12-02 20:56:16 +01:00
async function ( ) {
await delay ( 50 ) ;
$ ( '#floatingPrompt' ) . removeClass ( 'resizing' ) ;
} ) ;
2023-07-20 19:32:15 +02:00
setTimeout ( function ( ) {
2023-12-02 19:04:51 +01:00
$ ( '#floatingPrompt' ) . hide ( ) ;
2023-12-10 19:02:25 +01:00
} , animation _duration ) ;
2023-07-20 19:32:15 +02:00
}
//duplicate options menu close handler from script.js
//because this listener takes priority
2023-12-10 19:02:25 +01:00
$ ( '#options' ) . stop ( ) . fadeOut ( animation _duration ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Select a character before trying to use Author\'s Note' , '' , { timeOut : 2000 } ) ;
2023-07-20 19:32:15 +02:00
}
}
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 ) ;
2024-03-23 17:45:37 +01:00
$ ( '#extension_floating_role' ) . on ( 'input' , onExtensionFloatingRoleInput ) ;
$ ( '#extension_default_role' ) . on ( 'input' , onExtensionDefaultRoleInput ) ;
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 ( ) {
2023-12-02 19:04:51 +01:00
$ ( '#floatingPrompt' ) . transition ( {
2023-07-20 19:32:15 +02:00
opacity : 0 ,
2023-12-10 19:02:25 +01:00
duration : animation _duration ,
2023-07-20 19:32:15 +02:00
easing : 'ease-in-out' ,
} ) ;
2023-12-10 19:02:25 +01:00
setTimeout ( function ( ) { $ ( '#floatingPrompt' ) . hide ( ) ; } , animation _duration ) ;
2023-12-02 20:11:06 +01:00
} ) ;
2023-12-02 19:04:51 +01:00
$ ( '#option_toggle_AN' ) . on ( 'click' , onANMenuItemClick ) ;
2023-07-20 19:32:15 +02:00
2023-12-02 19:04:51 +01:00
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 ) ;
2023-07-20 19:32:15 +02:00
eventSource . on ( event _types . CHAT _CHANGED , onChatChanged ) ;
2023-08-25 15:46:54 +02:00
}