2023-08-18 12:41:46 +02:00
import { callPopup , getCurrentChatId , reloadCurrentChat , saveSettingsDebounced } from "../../../script.js" ;
2023-07-20 19:32:15 +02:00
import { extension _settings } from "../../extensions.js" ;
2023-11-30 21:59:04 +01:00
import { registerSlashCommand } from "../../slash-commands.js" ;
2023-08-18 12:41:46 +02:00
import { getSortableDelay , uuidv4 } from "../../utils.js" ;
2023-11-30 21:59:04 +01:00
import { resolveVariable } from "../../variables.js" ;
import { regex _placement , runRegexScript } from "./engine.js" ;
2023-07-20 19:32:15 +02:00
async function saveRegexScript ( regexScript , existingScriptIndex ) {
// If not editing
2023-07-25 05:21:54 +02:00
// Is the script name undefined or empty?
if ( ! regexScript . scriptName ) {
toastr . error ( ` Could not save regex script: The script name was undefined or empty! ` ) ;
return ;
}
if ( existingScriptIndex === - 1 ) {
2023-07-20 19:32:15 +02:00
// Does the script name already exist?
if ( extension _settings . regex . find ( ( e ) => e . scriptName === regexScript . scriptName ) ) {
toastr . error ( ` Could not save regex script: A script with name ${ regexScript . scriptName } already exists. ` ) ;
return ;
}
} else {
// Does the script name already exist somewhere else?
// (If this fails, make it a .filter().map() to index array)
const foundIndex = extension _settings . regex . findIndex ( ( e ) => e . scriptName === regexScript . scriptName ) ;
if ( foundIndex !== existingScriptIndex && foundIndex !== - 1 ) {
toastr . error ( ` Could not save regex script: A script with name ${ regexScript . scriptName } already exists. ` ) ;
return ;
}
}
// Is a find regex present?
if ( regexScript . findRegex . length === 0 ) {
2023-07-25 05:21:54 +02:00
toastr . warning ( ` This regex script will not work, but was saved anyway: A find regex isn't present. ` ) ;
2023-07-20 19:32:15 +02:00
}
// Is there someplace to place results?
if ( regexScript . placement . length === 0 ) {
2023-07-25 05:21:54 +02:00
toastr . warning ( ` This regex script will not work, but was saved anyway: One "Affects" checkbox must be selected! ` ) ;
2023-07-20 19:32:15 +02:00
}
if ( existingScriptIndex !== - 1 ) {
extension _settings . regex [ existingScriptIndex ] = regexScript ;
} else {
extension _settings . regex . push ( regexScript ) ;
}
saveSettingsDebounced ( ) ;
await loadRegexScripts ( ) ;
// Reload the current chat to undo previous markdown
const currentChatId = getCurrentChatId ( ) ;
if ( currentChatId !== undefined && currentChatId !== null ) {
await reloadCurrentChat ( ) ;
}
}
async function deleteRegexScript ( { existingId } ) {
let scriptName = $ ( ` # ${ existingId } ` ) . find ( '.regex_script_name' ) . text ( ) ;
const existingScriptIndex = extension _settings . regex . findIndex ( ( script ) => script . scriptName === scriptName ) ;
if ( ! existingScriptIndex || existingScriptIndex !== - 1 ) {
extension _settings . regex . splice ( existingScriptIndex , 1 ) ;
saveSettingsDebounced ( ) ;
await loadRegexScripts ( ) ;
}
}
async function loadRegexScripts ( ) {
$ ( "#saved_regex_scripts" ) . empty ( ) ;
const scriptTemplate = $ ( await $ . get ( "scripts/extensions/regex/scriptTemplate.html" ) ) ;
extension _settings . regex . forEach ( ( script ) => {
// Have to clone here
const scriptHtml = scriptTemplate . clone ( ) ;
scriptHtml . attr ( 'id' , uuidv4 ( ) ) ;
scriptHtml . find ( '.regex_script_name' ) . text ( script . scriptName ) ;
2023-10-26 04:48:19 +02:00
scriptHtml . find ( '.disable_regex' ) . prop ( "checked" , script . disabled ? ? false )
2023-11-05 22:40:43 +01:00
. on ( 'input' , function ( ) {
script . disabled = ! ! $ ( this ) . prop ( "checked" ) ;
saveSettingsDebounced ( ) ;
2023-10-26 04:48:19 +02:00
} ) ;
2023-11-05 22:40:43 +01:00
scriptHtml . find ( '.regex-toggle-on' ) . on ( 'click' , function ( ) {
scriptHtml . find ( '.disable_regex' ) . prop ( "checked" , true ) . trigger ( 'input' ) ;
} ) ;
scriptHtml . find ( '.regex-toggle-off' ) . on ( 'click' , function ( ) {
scriptHtml . find ( '.disable_regex' ) . prop ( "checked" , false ) . trigger ( 'input' ) ;
} ) ;
scriptHtml . find ( '.edit_existing_regex' ) . on ( 'click' , async function ( ) {
2023-07-20 19:32:15 +02:00
await onRegexEditorOpenClick ( scriptHtml . attr ( "id" ) ) ;
} ) ;
2023-11-05 22:40:43 +01:00
scriptHtml . find ( '.delete_regex' ) . on ( 'click' , async function ( ) {
2023-11-05 22:44:28 +01:00
const confirm = await callPopup ( "Are you sure you want to delete this regex script?" , "confirm" ) ;
if ( ! confirm ) {
return ;
}
2023-07-20 19:32:15 +02:00
await deleteRegexScript ( { existingId : scriptHtml . attr ( "id" ) } ) ;
} ) ;
$ ( "#saved_regex_scripts" ) . append ( scriptHtml ) ;
} ) ;
}
async function onRegexEditorOpenClick ( existingId ) {
const editorHtml = $ ( await $ . get ( "scripts/extensions/regex/editor.html" ) ) ;
// If an ID exists, fill in all the values
let existingScriptIndex = - 1 ;
if ( existingId ) {
const existingScriptName = $ ( ` # ${ existingId } ` ) . find ( '.regex_script_name' ) . text ( ) ;
existingScriptIndex = extension _settings . regex . findIndex ( ( script ) => script . scriptName === existingScriptName ) ;
if ( existingScriptIndex !== - 1 ) {
const existingScript = extension _settings . regex [ existingScriptIndex ] ;
if ( existingScript . scriptName ) {
editorHtml . find ( ` .regex_script_name ` ) . val ( existingScript . scriptName ) ;
} else {
toastr . error ( "This script doesn't have a name! Please delete it." )
return ;
}
editorHtml . find ( ` .find_regex ` ) . val ( existingScript . findRegex || "" ) ;
editorHtml . find ( ` .regex_replace_string ` ) . val ( existingScript . replaceString || "" ) ;
editorHtml . find ( ` .regex_trim_strings ` ) . val ( existingScript . trimStrings ? . join ( "\n" ) || [ ] ) ;
editorHtml
. find ( ` input[name="disabled"] ` )
. prop ( "checked" , existingScript . disabled ? ? false ) ;
editorHtml
. find ( ` input[name="only_format_display"] ` )
. prop ( "checked" , existingScript . markdownOnly ? ? false ) ;
2023-11-02 23:52:33 +01:00
editorHtml
. find ( ` input[name="only_format_prompt"] ` )
. prop ( "checked" , existingScript . promptOnly ? ? false ) ;
2023-07-20 19:32:15 +02:00
editorHtml
. find ( ` input[name="run_on_edit"] ` )
. prop ( "checked" , existingScript . runOnEdit ? ? false ) ;
editorHtml
. find ( ` input[name="substitute_regex"] ` )
. prop ( "checked" , existingScript . substituteRegex ? ? false ) ;
editorHtml
. find ( ` select[name="replace_strategy_select"] ` )
. val ( existingScript . replaceStrategy ? ? 0 ) ;
existingScript . placement . forEach ( ( element ) => {
editorHtml
. find ( ` input[name="replace_position"][value=" ${ element } "] ` )
. prop ( "checked" , true ) ;
} ) ;
}
} else {
editorHtml
. find ( ` input[name="only_format_display"] ` )
. prop ( "checked" , true ) ;
editorHtml
. find ( ` input[name="run_on_edit"] ` )
. prop ( "checked" , true ) ;
editorHtml
2023-07-25 05:21:54 +02:00
. find ( ` input[name="replace_position"][value="1"] ` )
2023-07-20 19:32:15 +02:00
. prop ( "checked" , true ) ;
}
const popupResult = await callPopup ( editorHtml , "confirm" , undefined , { okButton : "Save" } ) ;
if ( popupResult ) {
const newRegexScript = {
scriptName : editorHtml . find ( ".regex_script_name" ) . val ( ) ,
findRegex : editorHtml . find ( ".find_regex" ) . val ( ) ,
replaceString : editorHtml . find ( ".regex_replace_string" ) . val ( ) ,
trimStrings : editorHtml . find ( ".regex_trim_strings" ) . val ( ) . split ( "\n" ) . filter ( ( e ) => e . length !== 0 ) || [ ] ,
placement :
editorHtml
. find ( ` input[name="replace_position"] ` )
. filter ( ":checked" )
2023-11-05 22:40:43 +01:00
. map ( function ( ) { return parseInt ( $ ( this ) . val ( ) ) } )
2023-07-20 19:32:15 +02:00
. get ( )
2023-12-02 15:19:35 +01:00
. filter ( ( e ) => ! isNaN ( e ) ) || [ ] ,
2023-07-20 19:32:15 +02:00
disabled :
editorHtml
. find ( ` input[name="disabled"] ` )
. prop ( "checked" ) ,
markdownOnly :
editorHtml
. find ( ` input[name="only_format_display"] ` )
. prop ( "checked" ) ,
2023-11-02 23:52:33 +01:00
promptOnly :
editorHtml
. find ( ` input[name="only_format_prompt"] ` )
. prop ( "checked" ) ,
2023-07-20 19:32:15 +02:00
runOnEdit :
editorHtml
. find ( ` input[name="run_on_edit"] ` )
. prop ( "checked" ) ,
substituteRegex :
editorHtml
. find ( ` input[name="substitute_regex"] ` )
. prop ( "checked" ) ,
replaceStrategy :
parseInt ( editorHtml
. find ( ` select[name="replace_strategy_select"] ` )
. find ( ` :selected ` )
. val ( ) ) ? ? 0
} ;
saveRegexScript ( newRegexScript , existingScriptIndex ) ;
}
}
// Common settings migration function. Some parts will eventually be removed
// TODO: Maybe migrate placement to strings?
function migrateSettings ( ) {
let performSave = false ;
// Current: If MD Display is present in placement, remove it and add new placements/MD option
extension _settings . regex . forEach ( ( script ) => {
if ( script . placement . includes ( regex _placement . MD _DISPLAY ) ) {
script . placement = script . placement . length === 1 ?
Object . values ( regex _placement ) . filter ( ( e ) => e !== regex _placement . MD _DISPLAY ) :
script . placement = script . placement . filter ( ( e ) => e !== regex _placement . MD _DISPLAY ) ;
script . markdownOnly = true
2023-11-02 23:52:33 +01:00
script . promptOnly = true
2023-07-20 19:32:15 +02:00
performSave = true ;
}
// Old system and sendas placement migration
// 4 - sendAs
if ( script . placement . includes ( 4 ) ) {
script . placement = script . placement . length === 1 ?
[ regex _placement . SLASH _COMMAND ] :
script . placement = script . placement . filter ( ( e ) => e !== 4 ) ;
performSave = true ;
}
} ) ;
if ( performSave ) {
saveSettingsDebounced ( ) ;
}
}
2023-11-30 21:59:04 +01:00
/ * *
* / r e g e x s l a s h c o m m a n d c a l l b a c k
* @ param { object } args Named arguments
* @ param { string } value Unnamed argument
* @ returns { string } The regexed string
* /
function runRegexCallback ( args , value ) {
if ( ! args . name ) {
toastr . warning ( "No regex script name provided." ) ;
return value ;
}
const scriptName = String ( resolveVariable ( args . name ) ) ;
for ( const script of extension _settings . regex ) {
if ( String ( script . scriptName ) . toLowerCase ( ) === String ( scriptName ) . toLowerCase ( ) ) {
if ( script . disabled ) {
toastr . warning ( ` Regex script " ${ scriptName } " is disabled. ` ) ;
return value ;
}
console . debug ( ` Running regex callback for ${ scriptName } ` ) ;
return runRegexScript ( script , value ) ;
}
}
toastr . warning ( ` Regex script " ${ scriptName } " not found. ` ) ;
return value ;
}
2023-07-20 19:32:15 +02:00
// Workaround for loading in sequence with other extensions
// NOTE: Always puts extension at the top of the list, but this is fine since it's static
jQuery ( async ( ) => {
if ( extension _settings . regex ) {
migrateSettings ( ) ;
}
// Manually disable the extension since static imports auto-import the JS file
if ( extension _settings . disabledExtensions . includes ( "regex" ) ) {
return ;
}
const settingsHtml = await $ . get ( "scripts/extensions/regex/dropdown.html" ) ;
$ ( "#extensions_settings2" ) . append ( settingsHtml ) ;
2023-11-05 22:40:43 +01:00
$ ( "#open_regex_editor" ) . on ( "click" , function ( ) {
2023-07-20 19:32:15 +02:00
onRegexEditorOpenClick ( false ) ;
} ) ;
$ ( '#saved_regex_scripts' ) . sortable ( {
2023-08-18 12:41:46 +02:00
delay : getSortableDelay ( ) ,
2023-07-20 19:32:15 +02:00
stop : function ( ) {
let newScripts = [ ] ;
$ ( '#saved_regex_scripts' ) . children ( ) . each ( function ( ) {
const scriptName = $ ( this ) . find ( ".regex_script_name" ) . text ( ) ;
const existingScript = extension _settings . regex . find ( ( e ) => e . scriptName === scriptName ) ;
if ( existingScript ) {
newScripts . push ( existingScript ) ;
}
} ) ;
extension _settings . regex = newScripts ;
saveSettingsDebounced ( ) ;
console . debug ( "Regex scripts reordered" ) ;
// TODO: Maybe reload regex scripts after move
} ,
} ) ;
await loadRegexScripts ( ) ;
$ ( "#saved_regex_scripts" ) . sortable ( "enable" ) ;
2023-11-30 21:59:04 +01:00
registerSlashCommand ( 'regex' , runRegexCallback , [ ] , '(name=scriptName [input]) – runs a Regex extension script by name on the provided string. The script must be enabled.' , true , true ) ;
2023-07-20 19:32:15 +02:00
} ) ;