Merge branch 'SillyTavern:staging' into staging
This commit is contained in:
commit
141850eda5
|
@ -26,6 +26,8 @@ const extras = {
|
|||
captioningModel: 'Xenova/vit-gpt2-image-captioning',
|
||||
// Feature extraction model. HuggingFace ID of a model in ONNX format.
|
||||
embeddingModel: 'Xenova/all-mpnet-base-v2',
|
||||
// GPT-2 text generation model. HuggingFace ID of a model in ONNX format.
|
||||
promptExpansionModel: 'Cohee/fooocus_expansion-onnx',
|
||||
};
|
||||
|
||||
// Request overrides for additional headers
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
{
|
||||
"story_string": "{{#if system}}{{system}}\n{{/if}}{{#if wiBefore}}{{wiBefore}}\n{{/if}}{{#if description}}{{description}}\n{{/if}}{{#if personality}}{{personality}}\n{{/if}}{{#if scenario}}{{scenario}}\n{{/if}}{{#if wiAfter}}{{wiAfter}}\n{{/if}}{{#if persona}}{{persona}}\n{{/if}}",
|
||||
"chat_start": "",
|
||||
"example_separator": "",
|
||||
"chat_start": "",
|
||||
"always_force_name2": false,
|
||||
"trim_sentences": false,
|
||||
"include_newline": false,
|
||||
"custom_stopping_strings": "[\"\\n\"]",
|
||||
"custom_stopping_strings_macro": true,
|
||||
"name": "Adventure"
|
||||
}
|
|
@ -293,7 +293,7 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
#bg_menu_content {
|
||||
.bg_list {
|
||||
width: unset;
|
||||
}
|
||||
}
|
||||
|
@ -445,4 +445,4 @@
|
|||
#horde_model {
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,12 @@
|
|||
color: var(--fullred);
|
||||
}
|
||||
|
||||
.highlighted {
|
||||
color: black;
|
||||
background-color: yellow;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
.m-t-0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
|
|
@ -138,8 +138,7 @@
|
|||
filter: brightness(1);
|
||||
}
|
||||
|
||||
.tags_view,
|
||||
.open_alternate_greetings {
|
||||
.tags_view {
|
||||
margin: 0;
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
@ -171,4 +170,4 @@
|
|||
-1px 1px 0px black,
|
||||
1px -1px 0px black;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,9 @@
|
|||
<script type="module" src="scripts/filters.js"></script>
|
||||
<script type="module" src="scripts/personas.js"></script>
|
||||
<script type="module" src="scripts/server-history.js"></script>
|
||||
<script type="module" src="scripts/setting-search.js"></script>
|
||||
<script type="module" src="scripts/bulk-edit.js"></script>
|
||||
<script type="module" src="scripts/cfg-scale.js"></script>
|
||||
|
||||
<title>SillyTavern</title>
|
||||
</head>
|
||||
|
@ -127,8 +130,8 @@
|
|||
<span class="note-link-span">?</span>
|
||||
</a>
|
||||
</h4>
|
||||
<div class="preset_buttons">
|
||||
<select id="settings_perset" data-preset-manager-for="kobold">
|
||||
<div class="flex-container">
|
||||
<select id="settings_perset" data-preset-manager-for="kobold" class="flex1 text_pole">
|
||||
<option value="gui" data-i18n="guikoboldaisettings">GUI KoboldAI Settings</option>
|
||||
</select>
|
||||
<input type="file" hidden data-preset-manager-file="kobold" accept=".json, .settings">
|
||||
|
@ -146,8 +149,8 @@
|
|||
<span class="note-link-span">?</span>
|
||||
</a>
|
||||
</h4>
|
||||
<div class="preset_buttons">
|
||||
<select id="settings_perset_novel" data-preset-manager-for="novel">
|
||||
<div class="flex-container">
|
||||
<select id="settings_perset_novel" class="flex1 text_pole" data-preset-manager-for="novel">
|
||||
<option value="gui" data-i18n="default">Default</option>
|
||||
</select>
|
||||
<input type="file" hidden data-preset-manager-file="novel" accept=".json, .settings">
|
||||
|
@ -161,8 +164,8 @@
|
|||
<div id="openai_api-presets">
|
||||
<div>
|
||||
<h4 class="margin0"><span data-i18n="openaipresets">Chat Completion Presets</span></h4>
|
||||
<div class="openai_preset_buttons">
|
||||
<select id="settings_perset_openai">
|
||||
<div class="flex-container">
|
||||
<select id="settings_perset_openai" class="flex1 text_pole">
|
||||
<option value="gui" data-i18n="default">Default</option>
|
||||
</select>
|
||||
<i id="update_oai_preset" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
|
||||
|
@ -176,8 +179,8 @@
|
|||
</div>
|
||||
<div id="textgenerationwebui_api-presets">
|
||||
<h4 class="margin0"><span data-i18n="Text Gen WebUI (ooba/Mancer) presets">Text Gen WebUI (ooba/Mancer) presets</span></h4>
|
||||
<div class="preset_buttons">
|
||||
<select id="settings_preset_textgenerationwebui" data-preset-manager-for="textgenerationwebui">
|
||||
<div class="flex-container">
|
||||
<select id="settings_preset_textgenerationwebui" class="flex1 text_pole" data-preset-manager-for="textgenerationwebui">
|
||||
</select>
|
||||
<input type="file" hidden data-preset-manager-file="textgenerationwebui" accept=".json, .settings">
|
||||
<i data-newbie-hidden data-preset-manager-update="textgenerationwebui" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
|
||||
|
@ -232,7 +235,7 @@
|
|||
</div>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="max_context" name="volume" min="512" max="4096" step="1">
|
||||
<input type="range" id="max_context" name="volume" min="512" max="4096" step="512">
|
||||
</div>
|
||||
<div class="range-block-counter" data-randomization-disabled="true">
|
||||
<div contenteditable="true" data-for="max_context" id="max_context_counter">
|
||||
|
@ -1750,6 +1753,15 @@
|
|||
Add bias entry
|
||||
</div>
|
||||
<div class="openai_logit_bias_list"></div>
|
||||
<div class="m-t-1">
|
||||
<small>
|
||||
<i class="fa-solid fa-lightbulb"></i>
|
||||
|
||||
<span data-i18n="Most tokens have a leading space.">
|
||||
Most tokens have a leading space.
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2261,8 +2273,8 @@
|
|||
<h4 data-i18n="Context Template">
|
||||
Context Template
|
||||
</h4>
|
||||
<div class="preset_buttons flex-container">
|
||||
<select id="context_presets" data-preset-manager-for="context" class="flex1"></select>
|
||||
<div class="flex-container">
|
||||
<select id="context_presets" data-preset-manager-for="context" class="flex1 text_pole"></select>
|
||||
<input type="file" hidden data-preset-manager-file="context" accept=".json, .settings">
|
||||
<i id="context_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this preset for Instruct Mode."></i>
|
||||
<i data-newbie-hidden data-preset-manager-update="context" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
|
||||
|
@ -2349,8 +2361,8 @@
|
|||
<label for="instruct_presets">
|
||||
<span data-i18n="Presets">Presets</span>
|
||||
</label>
|
||||
<div class="preset_buttons">
|
||||
<select id="instruct_presets" data-preset-manager-for="instruct" class="flex1"></select>
|
||||
<div class="flex-container">
|
||||
<select id="instruct_presets" data-preset-manager-for="instruct" class="flex1 text_pole"></select>
|
||||
<input type="file" hidden data-preset-manager-file="instruct" accept=".json, .settings">
|
||||
<i id="instruct_set_default" class="menu_button fa-solid fa-heart" title="Auto-select this preset on API connection."></i>
|
||||
<i data-newbie-hidden data-preset-manager-update="instruct" class="menu_button fa-solid fa-save" title="Update current preset" data-i18n="[title]Update current preset"></i>
|
||||
|
@ -2512,18 +2524,6 @@
|
|||
Always add character's name to prompt
|
||||
</span>
|
||||
</label>
|
||||
<label data-newbie-hidden class="checkbox_label" for="pin-examples-checkbox">
|
||||
<input id="pin-examples-checkbox" type="checkbox" />
|
||||
<span data-i18n="Keep Example Messages in Prompt">
|
||||
Keep Example Messages in Prompt
|
||||
</span>
|
||||
</label>
|
||||
<label data-newbie-hidden class="checkbox_label" for="remove-examples-checkbox">
|
||||
<input id="remove-examples-checkbox" type="checkbox" />
|
||||
<span data-i18n="Strip Example Messages from Prompt">
|
||||
Strip Example Messages from Prompt
|
||||
</span>
|
||||
</label>
|
||||
<label class="checkbox_label" for="collapse-newlines-checkbox"><input id="collapse-newlines-checkbox" type="checkbox" />
|
||||
<span data-i18n="Remove Empty New Lines from Output">
|
||||
Remove Empty New Lines from Output
|
||||
|
@ -3076,7 +3076,19 @@
|
|||
<div name="UserSettingsThirdColumn" id="power-user-options-block" class="flex-container wide100p">
|
||||
<div id="power-user-option-checkboxes">
|
||||
<div data-newbie-hidden name="CharacterHandlingToggles">
|
||||
<h4>Character Handling</h4>
|
||||
<h4 data-i18n="Character Handling">
|
||||
Character Handling
|
||||
</h4>
|
||||
<div data-newbie-hidden id="examples-behavior-block">
|
||||
<label data-i18n="Example Messages Behavior">
|
||||
Example Messages Behavior:
|
||||
</label>
|
||||
<select id="example_messages_behavior">
|
||||
<option value="normal">Gradual push-out</option>
|
||||
<option value="keep">Always include examples</option>
|
||||
<option value="strip">Never include examples</option>
|
||||
</select>
|
||||
</div>
|
||||
<label data-newbie-hidden class="checkbox_label" for="fuzzy_search_checkbox">
|
||||
<input id="fuzzy_search_checkbox" type="checkbox" />
|
||||
<span data-i18n="Advanced Character Search">Advanced Character Search</span>
|
||||
|
@ -3131,10 +3143,16 @@
|
|||
Quick "Continue" button
|
||||
</span>
|
||||
</label>
|
||||
<label data-newbie-hidden class="checkbox_label" for="swipes-checkbox">
|
||||
<input id="swipes-checkbox" type="checkbox" />
|
||||
<span data-i18n="Swipes">Swipes</span>
|
||||
</label>
|
||||
<div class="checkbox-container flex-container">
|
||||
<label data-newbie-hidden class="checkbox_label" for="swipes-checkbox">
|
||||
<input id="swipes-checkbox" type="checkbox" />
|
||||
<span data-i18n="Swipes">Swipes</span>
|
||||
</label>
|
||||
<label data-newbie-hidden class="checkbox_label" for="gestures-checkbox">
|
||||
<input id="gestures-checkbox" type="checkbox" />
|
||||
<span data-i18n="Gestures">Gestures</span>
|
||||
</label>
|
||||
</div>
|
||||
<label class="checkbox_label" for="auto-load-chat-checkbox">
|
||||
<input id="auto-load-chat-checkbox" type="checkbox" />
|
||||
<span data-i18n="Auto-load Last Chat">Auto-load Last Chat</span>
|
||||
|
@ -3221,14 +3239,23 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="logo_block" class="drawer" title="Change Background Image" data-i18n="[title]Change Background Image">
|
||||
<div id="site_logo" class="drawer-toggle drawer-header">
|
||||
<div id="logo_block" class="drawer">
|
||||
<div id="site_logo" class="drawer-toggle drawer-header" title="Change Background Image" data-i18n="[title]Change Background Image">
|
||||
<div class="drawer-icon fa-solid fa-panorama closedIcon"></div>
|
||||
</div>
|
||||
<div class="drawer-content closedDrawer">
|
||||
<div id="Backgrounds" class="drawer-content closedDrawer">
|
||||
<div class="flex-container">
|
||||
<input id="bg-filter" placeholder="Filter" class="text_pole" type="search" />
|
||||
<div id="bg_menu_content">
|
||||
<div class="flex-container wide100p">
|
||||
<input id="bg-filter" placeholder="Filter" class="text_pole flex1" type="search" />
|
||||
<div id="auto_background" class="menu_button menu_button_icon" title="Automatically select a background based on the chat context.">
|
||||
<i class="fa-solid fa-wand-magic"></i>
|
||||
Auto-select
|
||||
</div>
|
||||
</div>
|
||||
<h3 data-i18n="System Backgrounds" class="wide100p textAlignCenter">
|
||||
System Backgrounds
|
||||
</h3>
|
||||
<div id="bg_menu_content" class="bg_list">
|
||||
<form id="form_bg_download" class="bg_example no-border no-shadow" action="javascript:void(null);" method="post" enctype="multipart/form-data">
|
||||
<label class="input-file">
|
||||
<input type="file" id="add_bg_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif, image/bmp">
|
||||
|
@ -3236,6 +3263,14 @@
|
|||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<h3 data-i18n="Chat Backgrounds" class="wide100p textAlignCenter">
|
||||
Chat Backgrounds
|
||||
</h3>
|
||||
<div id="bg_chat_hint" class="wide100p textAlignCenter">
|
||||
Chat backgrounds generated with the <code><i class="fa-solid fa-paintbrush"></i> Stable Diffusion</code> extension will appear here.
|
||||
</div>
|
||||
<div id="bg_custom_content" class="bg_list">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3247,28 +3282,42 @@
|
|||
<div id="rm_extensions_block" class="drawer-content closedDrawer">
|
||||
<div class="extensions_block flex-container">
|
||||
<div class="alignitemscenter flex-container justifyCenter wide100p" style="justify-content: space-between;">
|
||||
<h3 class="margin0" data-i18n="Extensions API:">Extensions API:
|
||||
<a target="_blank" href="https://github.com/SillyTavern/SillyTavern-extras">
|
||||
SillyTavern-extras
|
||||
<h3 class="margin0" data-i18n="Extras API:">Extras API:
|
||||
<a target="_blank" href="https://github.com/SillyTavern/SillyTavern-Extras">
|
||||
SillyTavern-Extras
|
||||
</a>
|
||||
</h3>
|
||||
<div class="flex-container">
|
||||
<div id="extensions_status" data-i18n="Not connected...">Not connected...</div>
|
||||
<label for="extensions_autoconnect">
|
||||
<label for="extensions_autoconnect" class="checkbox_label flexNoGap">
|
||||
<input id="extensions_autoconnect" type="checkbox">
|
||||
<span data-i18n="Auto-connect">Auto-connect</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alignitemsflexstart flex-container wide100p">
|
||||
<input id="extensions_url" type="text" class="flex1 heightFitContent text_pole widthNatural" maxlength="250" data-i18n="[placeholder]Extensions URL" placeholder="Extensions URL">
|
||||
<input id="extensions_api_key" type="text" class="flex1 heightFitContent text_pole widthNatural" maxlength="250" data-i18n="[placeholder]API key" placeholder="Extras API key">
|
||||
<input id="extensions_url" type="text" class="flex1 heightFitContent text_pole widthNatural" maxlength="250" data-i18n="[placeholder]Extras API URL" placeholder="Extras API URL">
|
||||
<input id="extensions_api_key" type="text" class="flex1 heightFitContent text_pole widthNatural" maxlength="250" data-i18n="[placeholder]Extras API key (optional)" placeholder="Extras API key (optional)">
|
||||
<div class="extensions_url_block">
|
||||
<div id="extensions_connect" class="menu_button" data-i18n="Connect">Connect</div>
|
||||
<div id="extensions_details" class="menu_button_icon menu_button">
|
||||
Manage extensions
|
||||
</div>
|
||||
<div id="third_party_extension_button" title="Import Extension From Git Repo" class="menu_button fa-solid fa-cloud-arrow-down faSmallFontSquareFix"></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="wide100p margin0">
|
||||
<div class="alignitemscenter flex-container wide100p">
|
||||
<h3 class="margin0 flex1" data-i18n="Extensions">
|
||||
Extensions
|
||||
</h3>
|
||||
<label for="extensions_notify_updates" class="checkbox_label flexNoGap">
|
||||
<input id="extensions_notify_updates" type="checkbox">
|
||||
<span data-i18n="Notify on extension updates">Notify on extension updates</span>
|
||||
</label>
|
||||
<div id="extensions_details" class="menu_button_icon menu_button">
|
||||
<i class="fa-solid fa-cubes"></i>
|
||||
Manage extensions
|
||||
</div>
|
||||
<div id="third_party_extension_button" title="Import Extension From Git Repo" class="menu_button menu_button_icon">
|
||||
<i class="fa-solid fa-cloud-arrow-down"></i>
|
||||
Install extension
|
||||
</div>
|
||||
</div>
|
||||
<div id="extensions_settings" class="flex1 wide50p">
|
||||
|
@ -3501,8 +3550,10 @@
|
|||
<span class="note-link-span">?</span>
|
||||
</a>
|
||||
</span>
|
||||
<div class="menu_button open_alternate_greetings" title="Click to set additional greeting messages" data-i18n="[title]Click to set additional greeting messages">
|
||||
<i class="fa-solid fa-comment-dots"></i>
|
||||
<div class="menu_button menu_button_icon open_alternate_greetings margin0" title="Click to set additional greeting messages" data-i18n="[title]Click to set additional greeting messages">
|
||||
<span data-i18n="Alt. Greetings">
|
||||
Alt. Greetings
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="firstmessage_textarea" data-i18n="[placeholder]This will be the first message from the character that starts every chat." placeholder="This will be the first message from the character that starts every chat." class="marginBot5" name="first_mes" placeholder=""></textarea>
|
||||
|
@ -3679,6 +3730,8 @@
|
|||
|
||||
<div id="rm_print_characters_pagination">
|
||||
<i id="charListGridToggle" class="fa-solid fa-table-cells-large menu_button" title="Toggle character grid view"></i>
|
||||
<i id="bulkEditButton" class="fa-solid fa-edit menu_button bulkEditButton" title="Bulk edit characters"></i>
|
||||
<i id="bulkDeleteButton" class="fa-solid fa-trash menu_button bulkDeleteButton" title="Bulk delete characters" style="display: none;"></i>
|
||||
</div>
|
||||
<div id="rm_print_characters_block" class="flexFlowColumn"></div>
|
||||
</div>
|
||||
|
@ -3874,8 +3927,11 @@
|
|||
|
||||
<div id="background_template" class="template_element">
|
||||
<div class="bg_example flex-container" bgfile="" class="bg_example_img" title="">
|
||||
<div title="Rename background" bgfile="" class="bg_button bg_example_edit fa-solid fa-pencil"></div>
|
||||
<div title="Delete background" bgfile="" class="bg_button bg_example_cross fa-solid fa-circle-xmark"></div>
|
||||
<div title="Copy to system backgrounds" class="bg_button bg_example_copy fa-solid fa-file-arrow-up"></div>
|
||||
<div title="Rename background" class="bg_button bg_example_edit fa-solid fa-pencil"></div>
|
||||
<div title="Lock" class="bg_button bg_example_lock fa-solid fa-lock"></div>
|
||||
<div title="Unlock" class="bg_button bg_example_unlock fa-solid fa-lock-open"></div>
|
||||
<div title="Delete background" class="bg_button bg_example_cross fa-solid fa-circle-xmark"></div>
|
||||
<div class="BGSampleTitle">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4316,7 +4372,7 @@
|
|||
|
||||
<div id="openai_logit_bias_template" class="template_element">
|
||||
<div class="openai_logit_bias_form">
|
||||
<input class="openai_logit_bias_text text_pole" data-i18n="[placeholder]Type here..." placeholder="type here..." />
|
||||
<input class="openai_logit_bias_text text_pole" data-i18n="[placeholder]Text or token ids" placeholder="Text or [token ids]" />
|
||||
<input class="openai_logit_bias_value text_pole" type="number" min="-100" value="0" max="100" />
|
||||
<i class="menu_button fa-solid fa-xmark openai_logit_bias_remove"></i>
|
||||
</form>
|
||||
|
@ -4716,6 +4772,178 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cfgConfig" class="drawer-content flexGap5">
|
||||
<div class="panelControlBar flex-container">
|
||||
<div id="cfgConfigHeader" class="fa-solid fa-grip drag-grabber"></div>
|
||||
<div id="CFGClose" class="fa-solid fa-circle-xmark"></div>
|
||||
</div>
|
||||
<div name="cfgConfigHolder" class="scrollY">
|
||||
<div id="chat_cfg_container">
|
||||
<div class="inline-drawer">
|
||||
<div id="CFGBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Chat CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small>
|
||||
<b>Unique to this chat.</b><br>
|
||||
</small>
|
||||
<label for="chat_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="chat_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="chat_cfg_guidance_scale" id="chat_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="chat_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="chat_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="chat_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="chat_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
<div id="groupchat_cfg_use_chara_container">
|
||||
<label class="checkbox_label" for="groupchat_cfg_use_chara">
|
||||
<input type="checkbox" id="groupchat_cfg_use_chara" />
|
||||
<span data-i18n="Use character CFG scales">Use character CFG scales</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chara_cfg_container" style="display: none;">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="charaANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Character CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small><b>Will be automatically added as the CFG for this character.</b></small>
|
||||
<br />
|
||||
<label for="chara_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="chara_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="chara_cfg_guidance_scale" id="chara_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="chara_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="chara_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="chara_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="global_cfg_container">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Global CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small><b>Will be used as the default CFG options for every chat unless overridden.</b></small>
|
||||
<br />
|
||||
<label for="global_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="global_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="global_cfg_guidance_scale" id="global_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="global_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="global_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="global_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="global_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cfg_prompt_combine_container">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>CFG Prompt Cascading</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<small>
|
||||
<b>Combine positive/negative prompts from other boxes.</b>
|
||||
<br />
|
||||
For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.
|
||||
</small>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<label for="cfg_prompt_combine">
|
||||
<span data-i18n="Scale">Always Include</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="0" />
|
||||
<span data-i18n="Chat Negatives">Chat Negatives</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="1" />
|
||||
<span data-i18n="Character Negatives">Character Negatives</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="2" />
|
||||
<span data-i18n="Global Negatives">Global Negatives</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<label>
|
||||
Custom Separator: <input id="cfg_prompt_separator" class="text_pole textarea_compact widthUnset" placeholder=""\n"" type="text" />
|
||||
</label>
|
||||
<label>
|
||||
Insertion Depth: <input id="cfg_prompt_insertion_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sheld">
|
||||
<div id="sheldheader" class="fa-solid fa-grip drag-grabber"></div>
|
||||
|
@ -4755,9 +4983,10 @@
|
|||
<i class="fa-lg fa-solid fa-note-sticky"></i>
|
||||
<span data-i18n="Author's Note">Author's Note</span>
|
||||
</a>
|
||||
<div data-newbie-hidden id="options_advanced">
|
||||
|
||||
</div>
|
||||
<a data-newbie-hidden id="option_toggle_CFG">
|
||||
<i class="fa-lg fa-solid fa-scale-balanced"></i>
|
||||
<span data-i18n="CFG Scale">CFG Scale</span>
|
||||
</a>
|
||||
<a id="option_back_to_main">
|
||||
<i class="fa-lg fa-solid fa-left-long"></i>
|
||||
<span data-i18n="Back to parent chat">Back to parent chat</span>
|
||||
|
|
304
public/script.js
304
public/script.js
|
@ -1,5 +1,5 @@
|
|||
import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, } from "./scripts/RossAscends-mods.js";
|
||||
import { userStatsHandler, statMesProcess } from './scripts/stats.js';
|
||||
import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js';
|
||||
import {
|
||||
generateKoboldWithStreaming,
|
||||
kai_settings,
|
||||
|
@ -142,7 +142,7 @@ import {
|
|||
onlyUnique,
|
||||
} from "./scripts/utils.js";
|
||||
|
||||
import { extension_settings, getContext, installExtension, loadExtensionSettings, processExtensionHelpers, registerExtensionHelper, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js";
|
||||
import { extension_settings, getContext, loadExtensionSettings, processExtensionHelpers, registerExtensionHelper, runGenerationInterceptors, saveMetadataDebounced } from "./scripts/extensions.js";
|
||||
import { COMMENT_NAME_DEFAULT, executeSlashCommands, getSlashCommandsHelp, registerSlashCommand } from "./scripts/slash-commands.js";
|
||||
import {
|
||||
tag_map,
|
||||
|
@ -169,7 +169,7 @@ import { getDeviceInfo } from "./scripts/RossAscends-mods.js";
|
|||
import { registerPromptManagerMigration } from "./scripts/PromptManager.js";
|
||||
import { getRegexedString, regex_placement } from "./scripts/extensions/regex/engine.js";
|
||||
import { FILTER_TYPES, FilterHelper } from "./scripts/filters.js";
|
||||
import { getCfgPrompt, getGuidanceScale } from "./scripts/extensions/cfg/util.js";
|
||||
import { getCfgPrompt, getGuidanceScale, initCfg } from "./scripts/cfg-scale.js";
|
||||
import {
|
||||
force_output_sequence,
|
||||
formatInstructModeChat,
|
||||
|
@ -182,6 +182,7 @@ import {
|
|||
import { applyLocale } from "./scripts/i18n.js";
|
||||
import { getTokenCount, getTokenizerModel, saveTokenCache } from "./scripts/tokenizers.js";
|
||||
import { initPersonas, selectCurrentPersona, setPersonaDescription } from "./scripts/personas.js";
|
||||
import { getBackgrounds, initBackgrounds } from "./scripts/backgrounds.js";
|
||||
|
||||
//exporting functions and vars for mods
|
||||
export {
|
||||
|
@ -319,7 +320,6 @@ reloadMarkdownProcessor();
|
|||
console.debug('initializing Prompt Itemization Array on Startup');
|
||||
let itemizedPrompts = [];
|
||||
|
||||
/* let bg_menu_toggle = false; */
|
||||
export const systemUserName = "SillyTavern System";
|
||||
let default_user_name = "User";
|
||||
let name1 = default_user_name;
|
||||
|
@ -348,7 +348,6 @@ let generation_started = new Date();
|
|||
let characters = [];
|
||||
let this_chid;
|
||||
let saveCharactersPage = 0;
|
||||
let backgrounds = [];
|
||||
const default_avatar = "img/ai4.png";
|
||||
export const system_avatar = "img/five.png";
|
||||
export const comment_avatar = "img/quill.png";
|
||||
|
@ -632,7 +631,6 @@ let create_save = {
|
|||
let animation_duration = 125;
|
||||
let animation_easing = "ease-in-out";
|
||||
let popup_type = "";
|
||||
let bg_file_for_del = "";
|
||||
let chat_file_for_del = "";
|
||||
let online_status = "no_connection";
|
||||
|
||||
|
@ -721,9 +719,12 @@ async function firstLoadInit() {
|
|||
await getUserAvatars();
|
||||
await getCharacters();
|
||||
await getBackgrounds();
|
||||
initBackgrounds();
|
||||
initAuthorsNote();
|
||||
initPersonas();
|
||||
initRossMods();
|
||||
initStats();
|
||||
initCfg();
|
||||
}
|
||||
|
||||
function checkOnlineStatus() {
|
||||
|
@ -1033,73 +1034,6 @@ async function getCharacters() {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
$("#bg_menu_content").children('div').remove();
|
||||
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",
|
||||
|
@ -1742,7 +1676,7 @@ function substituteParams(content, _name1, _name2, _original, _group) {
|
|||
if (typeof _original === 'string') {
|
||||
content = content.replace(/{{original}}/i, _original);
|
||||
}
|
||||
content = content.replace(/{{input}}/gi, $('#send_textarea').val());
|
||||
content = content.replace(/{{input}}/gi, String($('#send_textarea').val()));
|
||||
content = content.replace(/{{user}}/gi, _name1);
|
||||
content = content.replace(/{{char}}/gi, _name2);
|
||||
content = content.replace(/{{charIfNotGroup}}/gi, _group);
|
||||
|
@ -1753,11 +1687,14 @@ function substituteParams(content, _name1, _name2, _original, _group) {
|
|||
content = content.replace(/<CHARIFNOTGROUP>/gi, _group);
|
||||
content = content.replace(/<GROUP>/gi, _group);
|
||||
|
||||
content = content.replace(/\{\{\/\/(.*?)\}\}/g, "");
|
||||
|
||||
content = content.replace(/{{time}}/gi, moment().format('LT'));
|
||||
content = content.replace(/{{date}}/gi, moment().format('LL'));
|
||||
content = content.replace(/{{weekday}}/gi, moment().format('dddd'));
|
||||
content = content.replace(/{{isotime}}/gi, moment().format('HH:mm'));
|
||||
content = content.replace(/{{isodate}}/gi, moment().format('YYYY-MM-DD'));
|
||||
|
||||
content = content.replace(/{{datetimeformat +([^}]*)}}/gi, (_, format) => {
|
||||
const formattedTime = moment().format(format);
|
||||
return formattedTime;
|
||||
|
@ -2185,11 +2122,12 @@ class StreamingProcessor {
|
|||
let processedText = cleanUpMessage(text, isImpersonate, isContinue, !isFinal);
|
||||
|
||||
// Predict unbalanced asterisks / quotes during streaming
|
||||
const charsToBalance = ['*', '"'];
|
||||
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;
|
||||
const separator = char.length > 1 ? '\n' : '';
|
||||
processedText = processedText.trimEnd() + separator + char;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3080,7 +3018,9 @@ async function Generate(type, { automatic_trigger, force_name2, resolve, reject,
|
|||
: ` ${cfgPrompt.value}`;
|
||||
} else {
|
||||
// TODO: Make all extension prompts use an array/splice method
|
||||
finalMesSend[mesSend.length - cfgPrompt.depth].extensionPrompts.push(`${cfgPrompt.value}\n`);
|
||||
const lengthDiff = mesSend.length - cfgPrompt.depth;
|
||||
const cfgDepth = lengthDiff >= 0 ? lengthDiff : 0;
|
||||
finalMesSend[cfgDepth].extensionPrompts.push(`${cfgPrompt.value}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4819,8 +4759,6 @@ export async function getUserAvatars() {
|
|||
});
|
||||
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>');
|
||||
|
||||
|
@ -4883,9 +4821,9 @@ export function setUserName(value) {
|
|||
function setUserAvatar() {
|
||||
user_avatar = $(this).attr("imgfile");
|
||||
reloadUserAvatar();
|
||||
saveSettingsDebounced();
|
||||
highlightSelectedAvatar();
|
||||
selectCurrentPersona();
|
||||
saveSettingsDebounced();
|
||||
$('.zoomed_avatar[forchar]').remove();
|
||||
}
|
||||
|
||||
|
@ -5932,48 +5870,6 @@ function callPopup(text, type, inputValue = '', { okButton, rows, wide, large }
|
|||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -6187,6 +6083,7 @@ function enlargeMessageImage() {
|
|||
const mesId = mesBlock.attr('mesid');
|
||||
const message = chat[mesId];
|
||||
const imgSrc = message?.extra?.image;
|
||||
const title = message?.extra?.title;
|
||||
|
||||
if (!imgSrc) {
|
||||
return;
|
||||
|
@ -6195,7 +6092,12 @@ function enlargeMessageImage() {
|
|||
const img = document.createElement('img');
|
||||
img.classList.add('img_enlarged');
|
||||
img.src = imgSrc;
|
||||
callPopup(img.outerHTML, 'text', '', { wide: true, large: true });
|
||||
const imgContainer = $('<div><pre><code></code></pre></div>');
|
||||
imgContainer.prepend(img);
|
||||
imgContainer.addClass('img_enlarged_container');
|
||||
imgContainer.find('code').addClass('txt').text(title);
|
||||
addCopyToCodeBlocks(imgContainer);
|
||||
callPopup(imgContainer, 'text', '', { wide: true, large: true });
|
||||
}
|
||||
|
||||
function updateAlternateGreetingsHintVisibility(root) {
|
||||
|
@ -7340,86 +7242,6 @@ jQuery(async function () {
|
|||
});
|
||||
$("#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);
|
||||
|
||||
if (!new_bg_extensionless) {
|
||||
console.debug('no new_bg_extensionless');
|
||||
return;
|
||||
}
|
||||
|
||||
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',
|
||||
headers: getRequestHeaders(),
|
||||
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);
|
||||
|
@ -7476,10 +7298,6 @@ jQuery(async function () {
|
|||
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();
|
||||
|
@ -7574,10 +7392,6 @@ jQuery(async function () {
|
|||
|
||||
});
|
||||
|
||||
$("#add_bg_button").change(function () {
|
||||
read_bg_load(this);
|
||||
});
|
||||
|
||||
$("#add_avatar_button").change(function () {
|
||||
read_avatar_load(this);
|
||||
});
|
||||
|
@ -7917,6 +7731,7 @@ jQuery(async function () {
|
|||
$("#rm_button_selected_ch").children("h2").text('');
|
||||
select_rm_characters();
|
||||
sendSystemMessage(system_message_types.WELCOME);
|
||||
eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
|
||||
} else {
|
||||
toastr.info("Please stop the message generation first.");
|
||||
}
|
||||
|
@ -8268,6 +8083,33 @@ jQuery(async function () {
|
|||
}, 150);
|
||||
})
|
||||
|
||||
$(document).on("click", function (e) {
|
||||
// Expanded options don't need to be closed
|
||||
if (power_user.expand_message_actions) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the click was outside the relevant elements
|
||||
if (!$(e.target).closest('.extraMesButtons, .extraMesButtonsHint').length) {
|
||||
// Transition out the .extraMesButtons first
|
||||
$('.extraMesButtons:visible').transition({
|
||||
opacity: 0,
|
||||
duration: 150,
|
||||
easing: 'ease-in-out',
|
||||
complete: function () {
|
||||
$(this).hide(); // Hide the .extraMesButtons after the transition
|
||||
|
||||
// Transition the .extraMesButtonsHint back in
|
||||
$('.extraMesButtonsHint:not(:visible)').show().transition({
|
||||
opacity: .2,
|
||||
duration: 150,
|
||||
easing: 'ease-in-out'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on("click", ".mes_edit_cancel", function () {
|
||||
let text = chat[this_edit_mes_id]["mes"];
|
||||
|
||||
|
@ -8818,18 +8660,6 @@ jQuery(async function () {
|
|||
}
|
||||
});
|
||||
|
||||
$("#bg-filter").on("input", function () {
|
||||
const filterValue = String($(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) {
|
||||
|
@ -8974,34 +8804,6 @@ jQuery(async function () {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 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 '/api/extensions/install' 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> Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.</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();
|
||||
await installExtension(url);
|
||||
});
|
||||
|
||||
|
||||
const $dropzone = $(document.body);
|
||||
|
||||
$dropzone.on('dragover', (event) => {
|
||||
|
|
|
@ -18,6 +18,8 @@ import {
|
|||
getThumbnailUrl,
|
||||
selectCharacterById,
|
||||
eventSource,
|
||||
menu_type,
|
||||
substituteParams,
|
||||
} from "../script.js";
|
||||
|
||||
import {
|
||||
|
@ -234,7 +236,9 @@ export function RA_CountCharTokens() {
|
|||
total_tokens += Number(counter.text());
|
||||
permanent_tokens += isPermanent ? Number(counter.text()) : 0;
|
||||
} else {
|
||||
const tokens = getTokenCount(value);
|
||||
// We substitute macro for existing characters, but not for the character being created
|
||||
const valueToCount = menu_type === 'create' ? value : substituteParams(value);
|
||||
const tokens = getTokenCount(valueToCount);
|
||||
counter.text(tokens);
|
||||
total_tokens += tokens;
|
||||
permanent_tokens += isPermanent ? tokens : 0;
|
||||
|
@ -897,6 +901,9 @@ export function initRossMods() {
|
|||
//Regenerate if user swipes on the last mesage in chat
|
||||
|
||||
document.addEventListener('swiped-left', function (e) {
|
||||
if (power_user.gestures === false) {
|
||||
return
|
||||
}
|
||||
var SwipeButR = $('.swipe_right:last');
|
||||
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||
if (SwipeTargetMesClassParent !== null) {
|
||||
|
@ -906,6 +913,9 @@ export function initRossMods() {
|
|||
}
|
||||
});
|
||||
document.addEventListener('swiped-right', function (e) {
|
||||
if (power_user.gestures === false) {
|
||||
return
|
||||
}
|
||||
var SwipeButL = $('.swipe_left:last');
|
||||
var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
|
||||
if (SwipeTargetMesClassParent !== null) {
|
||||
|
|
|
@ -0,0 +1,488 @@
|
|||
import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl } from "../script.js";
|
||||
import { saveMetadataDebounced } from "./extensions.js";
|
||||
import { registerSlashCommand } from "./slash-commands.js";
|
||||
import { stringFormat } from "./utils.js";
|
||||
|
||||
const BG_METADATA_KEY = 'custom_background';
|
||||
const LIST_METADATA_KEY = 'chat_backgrounds';
|
||||
|
||||
/**
|
||||
* Sets the background for the current chat and adds it to the list of custom backgrounds.
|
||||
* @param {{url: string, path:string}} backgroundInfo
|
||||
*/
|
||||
function forceSetBackground(backgroundInfo) {
|
||||
saveBackgroundMetadata(backgroundInfo.url);
|
||||
setCustomBackground();
|
||||
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const bg = backgroundInfo.path;
|
||||
list.push(bg);
|
||||
chat_metadata[LIST_METADATA_KEY] = list;
|
||||
saveMetadataDebounced();
|
||||
getChatBackgroundsList();
|
||||
highlightNewBackground(bg);
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
async function onChatChanged() {
|
||||
if (hasCustomBackground()) {
|
||||
setCustomBackground();
|
||||
}
|
||||
else {
|
||||
unsetCustomBackground();
|
||||
}
|
||||
|
||||
getChatBackgroundsList();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function getChatBackgroundsList() {
|
||||
const list = chat_metadata[LIST_METADATA_KEY];
|
||||
const listEmpty = !Array.isArray(list) || list.length === 0;
|
||||
|
||||
$('#bg_custom_content').empty();
|
||||
$('#bg_chat_hint').toggle(listEmpty);
|
||||
|
||||
if (listEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const bg of list) {
|
||||
const template = getBackgroundFromTemplate(bg, true);
|
||||
$('#bg_custom_content').append(template);
|
||||
}
|
||||
}
|
||||
|
||||
function getBackgroundPath(fileUrl) {
|
||||
return `backgrounds/${fileUrl}`;
|
||||
}
|
||||
|
||||
function highlightLockedBackground() {
|
||||
$('.bg_example').removeClass('locked');
|
||||
|
||||
const lockedBackground = chat_metadata[BG_METADATA_KEY];
|
||||
|
||||
if (!lockedBackground) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(`.bg_example`).each(function () {
|
||||
const url = $(this).data('url');
|
||||
if (url === lockedBackground) {
|
||||
$(this).addClass('locked');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onLockBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
const chatName = getCurrentChatId();
|
||||
|
||||
if (!chatName) {
|
||||
toastr.warning('Select a chat to lock the background for it');
|
||||
return;
|
||||
}
|
||||
|
||||
const relativeBgImage = getUrlParameter(this);
|
||||
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function onUnlockBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
function hasCustomBackground() {
|
||||
return chat_metadata[BG_METADATA_KEY];
|
||||
}
|
||||
|
||||
function saveBackgroundMetadata(file) {
|
||||
chat_metadata[BG_METADATA_KEY] = file;
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function removeBackgroundMetadata() {
|
||||
delete chat_metadata[BG_METADATA_KEY];
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function setCustomBackground() {
|
||||
const file = chat_metadata[BG_METADATA_KEY];
|
||||
|
||||
// bg already set
|
||||
if (document.getElementById("bg_custom").style.backgroundImage == file) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#bg_custom").css("background-image", file);
|
||||
}
|
||||
|
||||
function unsetCustomBackground() {
|
||||
$("#bg_custom").css("background-image", 'none');
|
||||
}
|
||||
|
||||
function onSelectBackgroundClick() {
|
||||
const isCustom = $(this).attr('custom') === 'true';
|
||||
const relativeBgImage = getUrlParameter(this);
|
||||
|
||||
// if clicked on upload button
|
||||
if (!relativeBgImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatically lock the background if it's custom or other background is locked
|
||||
if (hasCustomBackground() || isCustom) {
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
highlightLockedBackground();
|
||||
} else {
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
const customBg = window.getComputedStyle(document.getElementById('bg_custom')).backgroundImage;
|
||||
|
||||
// Custom background is set. Do not override the layer below
|
||||
if (customBg !== 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
const bgFile = $(this).attr("bgfile");
|
||||
const backgroundUrl = getBackgroundPath(bgFile);
|
||||
|
||||
// Fetching to browser memory to reduce flicker
|
||||
fetch(backgroundUrl).then(() => {
|
||||
$("#bg1").css("background-image", relativeBgImage);
|
||||
setBackground(bgFile);
|
||||
}).catch(() => {
|
||||
console.log('Background could not be set: ' + backgroundUrl);
|
||||
});
|
||||
}
|
||||
|
||||
async function onCopyToSystemBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
const bgNames = await getNewBackgroundName(this);
|
||||
|
||||
if (!bgNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bgFile = await fetch(bgNames.oldBg);
|
||||
|
||||
if (!bgFile.ok) {
|
||||
toastr.warning('Failed to copy background');
|
||||
return;
|
||||
}
|
||||
|
||||
const blob = await bgFile.blob();
|
||||
const file = new File([blob], bgNames.newBg);
|
||||
const formData = new FormData();
|
||||
formData.set('avatar', file);
|
||||
|
||||
uploadBackground(formData);
|
||||
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const index = list.indexOf(bgNames.oldBg);
|
||||
list.splice(index, 1);
|
||||
saveMetadataDebounced();
|
||||
getChatBackgroundsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the new background name from the user.
|
||||
* @param {Element} referenceElement
|
||||
* @returns {Promise<{oldBg: string, newBg: string}>}
|
||||
* */
|
||||
async function getNewBackgroundName(referenceElement) {
|
||||
const exampleBlock = $(referenceElement).closest('.bg_example');
|
||||
const isCustom = exampleBlock.attr('custom') === 'true';
|
||||
const oldBg = exampleBlock.attr('bgfile');
|
||||
|
||||
if (!oldBg) {
|
||||
console.debug('no bgfile');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileExtension = oldBg.split('.').pop();
|
||||
const fileNameBase = isCustom ? oldBg.split('/').pop() : oldBg;
|
||||
const oldBgExtensionless = fileNameBase.replace(`.${fileExtension}`, '');
|
||||
const newBgExtensionless = await callPopup('<h3>Enter new background name:</h3>', 'input', oldBgExtensionless);
|
||||
|
||||
if (!newBgExtensionless) {
|
||||
console.debug('no new_bg_extensionless');
|
||||
return;
|
||||
}
|
||||
|
||||
const newBg = `${newBgExtensionless}.${fileExtension}`;
|
||||
|
||||
if (oldBgExtensionless === newBgExtensionless) {
|
||||
console.debug('new_bg === old_bg');
|
||||
return;
|
||||
}
|
||||
|
||||
return { oldBg, newBg };
|
||||
}
|
||||
|
||||
async function onRenameBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
const bgNames = await getNewBackgroundName(this);
|
||||
|
||||
if (!bgNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = { old_bg: bgNames.oldBg, new_bg: bgNames.newBg };
|
||||
const response = await fetch('/renamebackground', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify(data),
|
||||
cache: 'no-cache',
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(bgNames.newBg);
|
||||
} else {
|
||||
toastr.warning('Failed to rename background');
|
||||
}
|
||||
}
|
||||
|
||||
async function onDeleteBackgroundClick(e) {
|
||||
e.stopPropagation();
|
||||
const bgToDelete = $(this).closest('.bg_example');
|
||||
const url = bgToDelete.data('url');
|
||||
const isCustom = bgToDelete.attr('custom') === 'true';
|
||||
const confirm = await callPopup("<h3>Delete the background?</h3>", 'confirm');
|
||||
const bg = bgToDelete.attr('bgfile');
|
||||
|
||||
if (confirm) {
|
||||
// If it's not custom, it's a built-in background. Delete it from the server
|
||||
if (!isCustom) {
|
||||
delBackground(bg);
|
||||
} else {
|
||||
const list = chat_metadata[LIST_METADATA_KEY] || [];
|
||||
const index = list.indexOf(bg);
|
||||
list.splice(index, 1);
|
||||
}
|
||||
|
||||
const siblingSelector = '.bg_example:not(#form_bg_download)';
|
||||
const nextBg = bgToDelete.next(siblingSelector);
|
||||
const prevBg = bgToDelete.prev(siblingSelector);
|
||||
const anyBg = $(siblingSelector);
|
||||
|
||||
if (nextBg.length > 0) {
|
||||
nextBg.trigger('click');
|
||||
} else if (prevBg.length > 0) {
|
||||
prevBg.trigger('click');
|
||||
} else {
|
||||
$(anyBg[Math.floor(Math.random() * anyBg.length)]).trigger('click');
|
||||
}
|
||||
|
||||
bgToDelete.remove();
|
||||
|
||||
if (url === chat_metadata[BG_METADATA_KEY]) {
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
highlightLockedBackground();
|
||||
}
|
||||
|
||||
if (isCustom) {
|
||||
getChatBackgroundsList();
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const autoBgPrompt = `Pause your roleplay and choose a location ONLY from the provided list that is the most suitable for the current scene. Do not output any other text:\n{0}`;
|
||||
|
||||
async function autoBackgroundCommand() {
|
||||
/** @type {HTMLElement[]} */
|
||||
const bgTitles = Array.from(document.querySelectorAll('#bg_menu_content .BGSampleTitle'));
|
||||
const options = bgTitles.map(x => ({ element: x, text: x.innerText.trim() })).filter(x => x.text.length > 0);
|
||||
if (options.length == 0) {
|
||||
toastr.warning('No backgrounds to choose from. Please upload some images to the "backgrounds" folder.');
|
||||
return;
|
||||
}
|
||||
|
||||
const list = options.map(option => `- ${option.text}`).join('\n');
|
||||
const prompt = stringFormat(autoBgPrompt, list);
|
||||
const reply = await generateQuietPrompt(prompt, false, false);
|
||||
const fuse = new Fuse(options, { keys: ['text'] });
|
||||
const bestMatch = fuse.search(reply, { limit: 1 });
|
||||
|
||||
if (bestMatch.length == 0) {
|
||||
toastr.warning('No match found. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('Automatically choosing background:', bestMatch);
|
||||
bestMatch[0].item.element.click();
|
||||
}
|
||||
|
||||
export 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);
|
||||
$("#bg_menu_content").children('div').remove();
|
||||
for (const bg of getData) {
|
||||
const template = getBackgroundFromTemplate(bg, false);
|
||||
$("#bg_menu_content").append(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the background
|
||||
* @param {Element} block
|
||||
* @returns {string} URL of the background
|
||||
*/
|
||||
function getUrlParameter(block) {
|
||||
return $(block).closest(".bg_example").data("url");
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a background template
|
||||
* @param {string} bg Path to background
|
||||
* @param {boolean} isCustom Whether the background is custom
|
||||
* @returns {JQuery<HTMLElement>} Background template
|
||||
*/
|
||||
function getBackgroundFromTemplate(bg, isCustom) {
|
||||
const template = $('#background_template .bg_example').clone();
|
||||
const thumbPath = isCustom ? bg : getThumbnailUrl('bg', bg);
|
||||
const url = isCustom ? `url("${encodeURI(bg)}")` : `url("${getBackgroundPath(bg)}")`;
|
||||
const title = isCustom ? bg.split('/').pop() : bg;
|
||||
const friendlyTitle = title.slice(0, title.lastIndexOf('.'));
|
||||
template.attr('title', title);
|
||||
template.attr('bgfile', bg);
|
||||
template.attr('custom', String(isCustom));
|
||||
template.data('url', url);
|
||||
template.css('background-image', `url('${thumbPath}')`);
|
||||
template.find('.BGSampleTitle').text(friendlyTitle);
|
||||
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,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
function onBackgroundUploadSelected() {
|
||||
const form = $("#form_bg_download").get(0);
|
||||
|
||||
if (!(form instanceof HTMLFormElement)) {
|
||||
console.error('form_bg_download is not a form');
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData(form);
|
||||
uploadBackground(formData);
|
||||
form.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a background to the server
|
||||
* @param {FormData} formData
|
||||
*/
|
||||
function uploadBackground(formData) {
|
||||
jQuery.ajax({
|
||||
type: "POST",
|
||||
url: "/downloadbackground",
|
||||
data: formData,
|
||||
beforeSend: function () {
|
||||
},
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
success: async function (bg) {
|
||||
setBackground(bg);
|
||||
$("#bg1").css("background-image", `url("${getBackgroundPath(bg)}"`);
|
||||
await getBackgrounds();
|
||||
highlightNewBackground(bg);
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
console.log(exception);
|
||||
console.log(jqXHR);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} bg
|
||||
*/
|
||||
function highlightNewBackground(bg) {
|
||||
const newBg = $(`.bg_example[bgfile="${bg}"]`);
|
||||
const scrollOffset = newBg.offset().top - newBg.parent().offset().top;
|
||||
$('#Backgrounds').scrollTop(scrollOffset);
|
||||
newBg.addClass('flash animated');
|
||||
setTimeout(() => newBg.removeClass('flash animated'), 2000);
|
||||
}
|
||||
|
||||
function onBackgroundFilterInput() {
|
||||
const filterValue = String($(this).val()).toLowerCase();
|
||||
$("#bg_menu_content > div").each(function () {
|
||||
const $bgContent = $(this);
|
||||
if ($bgContent.attr("title").toLowerCase().includes(filterValue)) {
|
||||
$bgContent.show();
|
||||
} else {
|
||||
$bgContent.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function initBackgrounds() {
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
|
||||
$(document).on("click", '.bg_example', onSelectBackgroundClick);
|
||||
$(document).on('click', '.bg_example_lock', onLockBackgroundClick);
|
||||
$(document).on('click', '.bg_example_unlock', onUnlockBackgroundClick);
|
||||
$(document).on('click', '.bg_example_edit', onRenameBackgroundClick);
|
||||
$(document).on("click", '.bg_example_cross', onDeleteBackgroundClick);
|
||||
$(document).on("click", '.bg_example_copy', onCopyToSystemBackgroundClick);
|
||||
$('#auto_background').on("click", autoBackgroundCommand);
|
||||
$("#add_bg_button").on('change', onBackgroundUploadSelected);
|
||||
$("#bg-filter").on("input", onBackgroundFilterInput);
|
||||
registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], "– locks a background for the currently selected chat", true, true);
|
||||
registerSlashCommand('unlockbg', onUnlockBackgroundClick, ['bgunlock'], '– unlocks a background for the currently selected chat', true, true);
|
||||
registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], '– automatically changes the background based on the chat context using the AI request prompt', true, true);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { characters, getCharacters, handleDeleteCharacter, callPopup } from "../../../script.js";
|
||||
import { characters, getCharacters, handleDeleteCharacter, callPopup } from "../script.js";
|
||||
|
||||
let is_bulk_edit = false;
|
||||
|
||||
|
@ -64,23 +64,6 @@ async function onDeleteButtonClick() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the bulk edit and delete buttons to the UI.
|
||||
*/
|
||||
function addButtons() {
|
||||
const editButton = $(
|
||||
"<i id='bulkEditButton' class='fa-solid fa-edit menu_button bulkEditButton' title='Bulk edit characters'></i>"
|
||||
);
|
||||
const deleteButton = $(
|
||||
"<i id='bulkDeleteButton' class='fa-solid fa-trash menu_button bulkDeleteButton' title='Bulk delete characters' style='display: none;'></i>"
|
||||
);
|
||||
|
||||
$("#charListGridToggle").after(editButton, deleteButton);
|
||||
|
||||
$("#bulkEditButton").on("click", onEditButtonClick);
|
||||
$("#bulkDeleteButton").on("click", onDeleteButtonClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables bulk selection by adding a checkbox next to each character.
|
||||
*/
|
||||
|
@ -111,7 +94,7 @@ function disableBulkSelect() {
|
|||
/**
|
||||
* Entry point that runs on page load.
|
||||
*/
|
||||
jQuery(async () => {
|
||||
addButtons();
|
||||
// loadSettings();
|
||||
jQuery(() => {
|
||||
$("#bulkEditButton").on("click", onEditButtonClick);
|
||||
$("#bulkDeleteButton").on("click", onDeleteButtonClick);
|
||||
});
|
|
@ -1,19 +1,17 @@
|
|||
import {
|
||||
chat_metadata,
|
||||
substituteParams,
|
||||
this_chid,
|
||||
eventSource,
|
||||
event_types,
|
||||
saveSettingsDebounced,
|
||||
this_chid,
|
||||
} from "../../../script.js";
|
||||
import { selected_group } from "../../group-chats.js";
|
||||
import { extension_settings, saveMetadataDebounced } from "../../extensions.js";
|
||||
import { getCharaFilename, delay } from "../../utils.js";
|
||||
import { power_user } from "../../power-user.js";
|
||||
import { metadataKeys } from "./util.js";
|
||||
} from "../script.js";
|
||||
import { extension_settings, saveMetadataDebounced } from "./extensions.js"
|
||||
import { selected_group } from "./group-chats.js";
|
||||
import { getCharaFilename, delay } from "./utils.js";
|
||||
import { power_user } from "./power-user.js";
|
||||
|
||||
// Keep track of where your extension is located, name should match repo name
|
||||
const extensionName = "cfg";
|
||||
const extensionFolderPath = `scripts/extensions/${extensionName}`;
|
||||
const extensionName = 'cfg';
|
||||
const defaultSettings = {
|
||||
global: {
|
||||
"guidance_scale": 1,
|
||||
|
@ -199,7 +197,7 @@ function loadSettings() {
|
|||
if (!promptSeparator.startsWith(`"`)) {
|
||||
promptSeparatorDisplay.unshift(`"`);
|
||||
}
|
||||
|
||||
|
||||
if (!promptSeparator.endsWith(`"`)) {
|
||||
promptSeparatorDisplay.push(`"`);
|
||||
}
|
||||
|
@ -279,14 +277,8 @@ function migrateSettings() {
|
|||
}
|
||||
|
||||
// This function is called when the extension is loaded
|
||||
jQuery(async () => {
|
||||
// This is an example of loading HTML from a file
|
||||
const windowHtml = $(await $.get(`${extensionFolderPath}/window.html`));
|
||||
|
||||
// Append settingsHtml to extensions_settings
|
||||
// extension_settings and extensions_settings2 are the left and right columns of the settings menu
|
||||
// Left should be extensions that deal with system functions and right should be visual/UI related
|
||||
windowHtml.find('#CFGClose').on('click', function () {
|
||||
export function initCfg() {
|
||||
$('#CFGClose').on('click', function () {
|
||||
$("#cfgConfig").transition({
|
||||
opacity: 0,
|
||||
duration: 200,
|
||||
|
@ -295,7 +287,7 @@ jQuery(async () => {
|
|||
setTimeout(function () { $('#cfgConfig').hide() }, 200);
|
||||
});
|
||||
|
||||
windowHtml.find('#chat_cfg_guidance_scale').on('input', function() {
|
||||
$('#chat_cfg_guidance_scale').on('input', function() {
|
||||
const numberValue = Number($(this).val());
|
||||
const success = setChatCfg(numberValue, settingType.guidance_scale);
|
||||
if (success) {
|
||||
|
@ -303,15 +295,15 @@ jQuery(async () => {
|
|||
}
|
||||
});
|
||||
|
||||
windowHtml.find('#chat_cfg_negative_prompt').on('input', function() {
|
||||
$('#chat_cfg_negative_prompt').on('input', function() {
|
||||
setChatCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#chat_cfg_positive_prompt').on('input', function() {
|
||||
$('#chat_cfg_positive_prompt').on('input', function() {
|
||||
setChatCfg($(this).val(), settingType.positive_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#chara_cfg_guidance_scale').on('input', function() {
|
||||
$('#chara_cfg_guidance_scale').on('input', function() {
|
||||
const value = $(this).val();
|
||||
const success = setCharCfg(value, settingType.guidance_scale);
|
||||
if (success) {
|
||||
|
@ -319,34 +311,34 @@ jQuery(async () => {
|
|||
}
|
||||
});
|
||||
|
||||
windowHtml.find('#chara_cfg_negative_prompt').on('input', function() {
|
||||
$('#chara_cfg_negative_prompt').on('input', function() {
|
||||
setCharCfg($(this).val(), settingType.negative_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#chara_cfg_positive_prompt').on('input', function() {
|
||||
$('#chara_cfg_positive_prompt').on('input', function() {
|
||||
setCharCfg($(this).val(), settingType.positive_prompt);
|
||||
});
|
||||
|
||||
windowHtml.find('#global_cfg_guidance_scale').on('input', function() {
|
||||
$('#global_cfg_guidance_scale').on('input', function() {
|
||||
extension_settings.cfg.global.guidance_scale = Number($(this).val());
|
||||
$('#global_cfg_guidance_scale_counter').text(extension_settings.cfg.global.guidance_scale.toFixed(2));
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find('#global_cfg_negative_prompt').on('input', function() {
|
||||
$('#global_cfg_negative_prompt').on('input', function() {
|
||||
extension_settings.cfg.global.negative_prompt = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find('#global_cfg_positive_prompt').on('input', function() {
|
||||
$('#global_cfg_positive_prompt').on('input', function() {
|
||||
extension_settings.cfg.global.positive_prompt = $(this).val();
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find(`input[name="cfg_prompt_combine"]`).on('input', function() {
|
||||
const values = windowHtml.find(`input[name="cfg_prompt_combine"]`)
|
||||
$(`input[name="cfg_prompt_combine"]`).on('input', function() {
|
||||
const values = $('#cfgConfig').find(`input[name="cfg_prompt_combine"]`)
|
||||
.filter(":checked")
|
||||
.map(function() { return parseInt($(this).val()) })
|
||||
.map(function() { return Number($(this).val()) })
|
||||
.get()
|
||||
.filter((e) => !Number.isNaN(e)) || [];
|
||||
|
||||
|
@ -354,17 +346,17 @@ jQuery(async () => {
|
|||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find(`#cfg_prompt_insertion_depth`).on('input', function() {
|
||||
$(`#cfg_prompt_insertion_depth`).on('input', function() {
|
||||
chat_metadata[metadataKeys.prompt_insertion_depth] = Number($(this).val());
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find(`#cfg_prompt_separator`).on('input', function() {
|
||||
$(`#cfg_prompt_separator`).on('input', function() {
|
||||
chat_metadata[metadataKeys.prompt_separator] = $(this).val();
|
||||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
windowHtml.find('#groupchat_cfg_use_chara').on('input', function() {
|
||||
$('#groupchat_cfg_use_chara').on('input', function() {
|
||||
const checked = !!$(this).prop('checked');
|
||||
chat_metadata[metadataKeys.groupchat_individual_chars] = checked
|
||||
|
||||
|
@ -375,20 +367,126 @@ jQuery(async () => {
|
|||
saveMetadataDebounced();
|
||||
});
|
||||
|
||||
$("#movingDivs").append(windowHtml);
|
||||
|
||||
initialLoadSettings();
|
||||
|
||||
if (extension_settings.cfg) {
|
||||
migrateSettings();
|
||||
}
|
||||
|
||||
const buttonHtml = $(await $.get(`${extensionFolderPath}/menuButton.html`));
|
||||
buttonHtml.on('click', onCfgMenuItemClick)
|
||||
buttonHtml.appendTo("#options_advanced");
|
||||
$('#option_toggle_CFG').on('click', onCfgMenuItemClick);
|
||||
|
||||
// Hook events
|
||||
eventSource.on(event_types.CHAT_CHANGED, async () => {
|
||||
await onChatChanged();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const cfgType = {
|
||||
chat: 0,
|
||||
chara: 1,
|
||||
global: 2
|
||||
}
|
||||
|
||||
export const metadataKeys = {
|
||||
guidance_scale: "cfg_guidance_scale",
|
||||
negative_prompt: "cfg_negative_prompt",
|
||||
positive_prompt: "cfg_positive_prompt",
|
||||
prompt_combine: "cfg_prompt_combine",
|
||||
groupchat_individual_chars: "cfg_groupchat_individual_chars",
|
||||
prompt_insertion_depth: "cfg_prompt_insertion_depth",
|
||||
prompt_separator: "cfg_prompt_separator"
|
||||
}
|
||||
|
||||
// Gets the CFG guidance scale
|
||||
// If the guidance scale is 1, ignore the CFG prompt(s) since it won't be used anyways
|
||||
export function getGuidanceScale() {
|
||||
if (!extension_settings.cfg) {
|
||||
console.warn("CFG extension is not enabled. Skipping CFG guidance.");
|
||||
return;
|
||||
}
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
|
||||
const groupchatCharOverride = chat_metadata[metadataKeys.groupchat_individual_chars] ?? false;
|
||||
|
||||
if (chatGuidanceScale && chatGuidanceScale !== 1 && !groupchatCharOverride) {
|
||||
return {
|
||||
type: cfgType.chat,
|
||||
value: chatGuidanceScale
|
||||
};
|
||||
}
|
||||
|
||||
if ((!selected_group && charaCfg || groupchatCharOverride) && charaCfg?.guidance_scale !== 1) {
|
||||
return {
|
||||
type: cfgType.chara,
|
||||
value: charaCfg.guidance_scale
|
||||
};
|
||||
}
|
||||
|
||||
if (extension_settings.cfg.global && extension_settings.cfg.global?.guidance_scale !== 1) {
|
||||
return {
|
||||
type: cfgType.global,
|
||||
value: extension_settings.cfg.global.guidance_scale
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the CFG prompt separator.
|
||||
* @returns {string} The CFG prompt separator
|
||||
*/
|
||||
function getCustomSeparator() {
|
||||
const defaultSeparator = "\n";
|
||||
|
||||
try {
|
||||
if (chat_metadata[metadataKeys.prompt_separator]) {
|
||||
return JSON.parse(chat_metadata[metadataKeys.prompt_separator]);
|
||||
}
|
||||
|
||||
return defaultSeparator;
|
||||
} catch {
|
||||
console.warn("Invalid JSON detected for prompt separator. Using default separator.");
|
||||
return defaultSeparator;
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the CFG prompt
|
||||
export function getCfgPrompt(guidanceScale, isNegative) {
|
||||
let splitCfgPrompt = [];
|
||||
|
||||
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
|
||||
if (guidanceScale.type === cfgType.chat || cfgPromptCombine.includes(cfgType.chat)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
chat_metadata[isNegative ? metadataKeys.negative_prompt : metadataKeys.positive_prompt]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||
if (guidanceScale.type === cfgType.chara || cfgPromptCombine.includes(cfgType.chara)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
isNegative ? charaCfg.negative_prompt : charaCfg.positive_prompt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (guidanceScale.type === cfgType.global || cfgPromptCombine.includes(cfgType.global)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
isNegative ? extension_settings.cfg.global.negative_prompt : extension_settings.cfg.global.positive_prompt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const customSeparator = getCustomSeparator();
|
||||
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
|
||||
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
|
||||
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
|
||||
|
||||
return {
|
||||
value: combinedCfgPrompt,
|
||||
depth: insertionDepth
|
||||
};
|
||||
}
|
|
@ -123,6 +123,7 @@ const extension_settings = {
|
|||
apiUrl: defaultUrl,
|
||||
apiKey: '',
|
||||
autoConnect: false,
|
||||
notifyUpdates: false,
|
||||
disabledExtensions: [],
|
||||
expressionOverrides: [],
|
||||
memory: {},
|
||||
|
@ -367,6 +368,15 @@ function addExtensionsButtonAndMenu() {
|
|||
});
|
||||
}
|
||||
|
||||
function notifyUpdatesInputHandler() {
|
||||
extension_settings.notifyUpdates = !!$('#extensions_notify_updates').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
||||
if (extension_settings.notifyUpdates) {
|
||||
checkForExtensionUpdates(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* $(document).on('click', function (e) {
|
||||
const target = $(e.target);
|
||||
if (target.is(dropdown)) return;
|
||||
|
@ -582,16 +592,25 @@ async function showExtensionsDetails() {
|
|||
let htmlExternal = '<h3>External Extensions:</h3>';
|
||||
|
||||
const extensions = Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order);
|
||||
const promises = [];
|
||||
|
||||
for (const extension of extensions) {
|
||||
const { isExternal, extensionHtml } = await getExtensionData(extension);
|
||||
if (isExternal) {
|
||||
htmlExternal += extensionHtml;
|
||||
} else {
|
||||
htmlDefault += extensionHtml;
|
||||
}
|
||||
promises.push(getExtensionData(extension));
|
||||
}
|
||||
|
||||
const settledPromises = await Promise.allSettled(promises);
|
||||
|
||||
settledPromises.forEach(promise => {
|
||||
if (promise.status === 'fulfilled') {
|
||||
const { isExternal, extensionHtml } = promise.value;
|
||||
if (isExternal) {
|
||||
htmlExternal += extensionHtml;
|
||||
} else {
|
||||
htmlDefault += extensionHtml;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const html = `
|
||||
${getModuleInformation()}
|
||||
${htmlDefault}
|
||||
|
@ -703,7 +722,9 @@ async function getExtensionVersion(extensionName) {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function installExtension(url) {
|
||||
console.debug('Extension import started', url);
|
||||
console.debug('Extension installation started', url);
|
||||
|
||||
toastr.info('Please wait...', 'Installing extension');
|
||||
|
||||
const request = await fetch('/api/extensions/install', {
|
||||
method: 'POST',
|
||||
|
@ -712,14 +733,14 @@ export async function installExtension(url) {
|
|||
});
|
||||
|
||||
if (!request.ok) {
|
||||
toastr.info(request.statusText, 'Extension import failed');
|
||||
console.error('Extension import failed', request.status, request.statusText);
|
||||
toastr.info(request.statusText, 'Extension installation failed');
|
||||
console.error('Extension installation 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}`);
|
||||
toastr.success(`Extension "${response.display_name}" by ${response.author} (version ${response.version}) has been installed successfully!`, 'Extension installation successful');
|
||||
console.debug(`Extension "${response.display_name}" has been installed successfully at ${response.extensionPath}`);
|
||||
await loadExtensionSettings({}, false);
|
||||
eventSource.emit(event_types.EXTENSION_SETTINGS_LOADED);
|
||||
}
|
||||
|
@ -737,6 +758,7 @@ async function loadExtensionSettings(settings, versionChanged) {
|
|||
$("#extensions_url").val(extension_settings.apiUrl);
|
||||
$("#extensions_api_key").val(extension_settings.apiKey);
|
||||
$("#extensions_autoconnect").prop('checked', extension_settings.autoConnect);
|
||||
$("#extensions_notify_updates").prop('checked', extension_settings.notifyUpdates);
|
||||
|
||||
// Activate offline extensions
|
||||
eventSource.emit(event_types.EXTENSIONS_FIRST_LOAD);
|
||||
|
@ -752,6 +774,55 @@ async function loadExtensionSettings(settings, versionChanged) {
|
|||
connectToApi(extension_settings.apiUrl);
|
||||
}
|
||||
|
||||
if (extension_settings.notifyUpdates) {
|
||||
checkForExtensionUpdates(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there are updates available for 3rd-party extensions.
|
||||
* @param {boolean} force Skip nag check
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
async function checkForExtensionUpdates(force) {
|
||||
if (!force) {
|
||||
const STORAGE_NAG_KEY = 'extension_update_nag';
|
||||
const currentDate = new Date().toDateString();
|
||||
|
||||
// Don't nag more than once a day
|
||||
if (localStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(STORAGE_NAG_KEY, currentDate);
|
||||
}
|
||||
|
||||
const updatesAvailable = [];
|
||||
const promises = [];
|
||||
|
||||
for (const [id, manifest] of Object.entries(manifests)) {
|
||||
if (manifest.auto_update && id.startsWith('third-party')) {
|
||||
const promise = new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const data = await getExtensionVersion(id.replace('third-party', ''));
|
||||
if (data.isUpToDate === false) {
|
||||
updatesAvailable.push(manifest.display_name);
|
||||
}
|
||||
resolve();
|
||||
} catch (error) {
|
||||
console.error('Error checking for extension updates', error);
|
||||
reject();
|
||||
}
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.allSettled(promises);
|
||||
|
||||
if (updatesAvailable.length > 0) {
|
||||
toastr.info(`${updatesAvailable.map(x => `• ${x}`).join('\n')}`, 'Extension updates available');
|
||||
}
|
||||
}
|
||||
|
||||
async function autoUpdateExtensions() {
|
||||
|
@ -783,8 +854,36 @@ jQuery(function () {
|
|||
$("#extensions_connect").on('click', connectClickHandler);
|
||||
$("#extensions_autoconnect").on('input', autoConnectInputHandler);
|
||||
$("#extensions_details").on('click', showExtensionsDetails);
|
||||
$("#extensions_notify_updates").on('input', notifyUpdatesInputHandler);
|
||||
$(document).on('click', '.toggle_disable', onDisableExtensionClick);
|
||||
$(document).on('click', '.toggle_enable', onEnableExtensionClick);
|
||||
$(document).on('click', '.btn_update', onUpdateClick);
|
||||
$(document).on('click', '.btn_delete', onDeleteClick);
|
||||
|
||||
/**
|
||||
* 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 '/api/extensions/install' 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 install</h3>
|
||||
<br>
|
||||
<p><b>Disclaimer:</b> Please be aware that using external extensions can have unintended side effects and may pose security risks. Always make sure you trust the source before importing an extension. We are not responsible for any damage caused by third-party extensions.</p>
|
||||
<br>
|
||||
<p>Example: <tt> https://github.com/author/extension-name </tt></p>`
|
||||
const input = await callPopup(html, 'input');
|
||||
|
||||
if (!input) {
|
||||
console.debug('Extension install cancelled');
|
||||
return;
|
||||
}
|
||||
|
||||
const url = input.trim();
|
||||
await installExtension(url);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,178 +0,0 @@
|
|||
import { eventSource, event_types, generateQuietPrompt } from "../../../script.js";
|
||||
import { getContext, saveMetadataDebounced } from "../../extensions.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
import { stringFormat } from "../../utils.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'backgrounds';
|
||||
const METADATA_KEY = 'custom_background';
|
||||
|
||||
/**
|
||||
* @param {string} background
|
||||
*/
|
||||
function forceSetBackground(background) {
|
||||
saveBackgroundMetadata(background);
|
||||
setCustomBackground();
|
||||
}
|
||||
|
||||
async function moduleWorker() {
|
||||
if (hasCustomBackground()) {
|
||||
$('#unlock_background').show();
|
||||
$('#lock_background').hide();
|
||||
setCustomBackground();
|
||||
}
|
||||
else {
|
||||
$('#unlock_background').hide();
|
||||
$('#lock_background').show();
|
||||
unsetCustomBackground();
|
||||
}
|
||||
}
|
||||
|
||||
function onLockBackgroundClick() {
|
||||
const bgImage = window.getComputedStyle(document.getElementById('bg1')).backgroundImage;
|
||||
|
||||
// Extract the URL from the CSS string
|
||||
const urlRegex = /url\((['"])?(.*?)\1\)/;
|
||||
const matches = bgImage.match(urlRegex);
|
||||
const url = matches[2];
|
||||
|
||||
// Remove the protocol and host, leaving the relative URL
|
||||
const relativeUrl = new URL(url).pathname;
|
||||
const relativeBgImage = `url("${relativeUrl}")`
|
||||
|
||||
saveBackgroundMetadata(relativeBgImage);
|
||||
setCustomBackground();
|
||||
$('#unlock_background').show();
|
||||
$('#lock_background').hide();
|
||||
}
|
||||
|
||||
function onUnlockBackgroundClick() {
|
||||
removeBackgroundMetadata();
|
||||
unsetCustomBackground();
|
||||
$('#unlock_background').hide();
|
||||
$('#lock_background').show();
|
||||
}
|
||||
|
||||
function hasCustomBackground() {
|
||||
const context = getContext();
|
||||
return !!context.chatMetadata[METADATA_KEY];
|
||||
}
|
||||
|
||||
function saveBackgroundMetadata(file) {
|
||||
const context = getContext();
|
||||
context.chatMetadata[METADATA_KEY] = file;
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function removeBackgroundMetadata() {
|
||||
const context = getContext();
|
||||
delete context.chatMetadata[METADATA_KEY];
|
||||
saveMetadataDebounced();
|
||||
}
|
||||
|
||||
function setCustomBackground() {
|
||||
const context = getContext();
|
||||
const file = context.chatMetadata[METADATA_KEY];
|
||||
|
||||
// bg already set
|
||||
if (document.getElementById("bg_custom").style.backgroundImage == file) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#bg_custom").css("background-image", file);
|
||||
$("#custom_bg_preview").css("background-image", file);
|
||||
}
|
||||
|
||||
function unsetCustomBackground() {
|
||||
$("#bg_custom").css("background-image", 'none');
|
||||
$("#custom_bg_preview").css("background-image", 'none');
|
||||
}
|
||||
|
||||
function onSelectBackgroundClick() {
|
||||
const bgfile = $(this).attr("bgfile");
|
||||
|
||||
if (hasCustomBackground()) {
|
||||
saveBackgroundMetadata(`url("backgrounds/${bgfile}")`);
|
||||
setCustomBackground();
|
||||
}
|
||||
}
|
||||
|
||||
const autoBgPrompt = `Pause your roleplay and choose a location ONLY from the provided list that is the most suitable for the current scene. Do not output any other text:\n{0}`;
|
||||
|
||||
async function autoBackgroundCommand() {
|
||||
const options = Array.from(document.querySelectorAll('.BGSampleTitle')).map(x => ({ element: x, text: x.innerText.trim() })).filter(x => x.text.length > 0);
|
||||
if (options.length == 0) {
|
||||
toastr.warning('No backgrounds to choose from. Please upload some images to the "backgrounds" folder.');
|
||||
return;
|
||||
}
|
||||
|
||||
const list = options.map(option => `- ${option.text}`).join('\n');
|
||||
const prompt = stringFormat(autoBgPrompt, list);
|
||||
const reply = await generateQuietPrompt(prompt);
|
||||
const fuse = new Fuse(options, { keys: ['text'] });
|
||||
const bestMatch = fuse.search(reply, { limit: 1 });
|
||||
|
||||
if (bestMatch.length == 0) {
|
||||
toastr.warning('No match found. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug('Automatically choosing background:', bestMatch);
|
||||
bestMatch[0].item.element.click();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
function addSettings() {
|
||||
const html = `
|
||||
<div class="background_settings">
|
||||
<div class="inline-drawer">
|
||||
<div class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Chat Backgrounds</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div class="background_controls">
|
||||
<div id="lock_background" class="menu_button">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
Lock
|
||||
</div>
|
||||
<div id="unlock_background" class="menu_button">
|
||||
<i class="fa-solid fa-unlock"></i>
|
||||
Unlock
|
||||
</div>
|
||||
<small>
|
||||
Press "Lock" to assign a currently selected background to a character or group chat.<br>
|
||||
Any background image selected while lock is engaged will be saved automatically.
|
||||
</small>
|
||||
</div>
|
||||
<div class="background_controls">
|
||||
<div id="auto_background" class="menu_button">
|
||||
<i class="fa-solid fa-wand-magic"></i>
|
||||
Auto
|
||||
</div>
|
||||
<small>
|
||||
Automatically select a background based on the chat context.<br>
|
||||
Respects the "Lock" setting state.
|
||||
</small>
|
||||
</div>
|
||||
<div>Preview</div>
|
||||
<div id="custom_bg_preview">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(html);
|
||||
$('#lock_background').on('click', onLockBackgroundClick);
|
||||
$('#unlock_background').on('click', onUnlockBackgroundClick);
|
||||
$(document).on("click", ".bg_example", onSelectBackgroundClick);
|
||||
$('#auto_background').on("click", autoBackgroundCommand);
|
||||
}
|
||||
|
||||
addSettings();
|
||||
registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], "– locks a background for the currently selected chat", true, true);
|
||||
registerSlashCommand('unlockbg', onUnlockBackgroundClick, ['bgunlock'], '– unlocks a background for the currently selected chat', true, true);
|
||||
registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], '– automatically changes the background based on the chat context using the AI request prompt', true, true);
|
||||
eventSource.on(event_types.FORCE_SET_BACKGROUND, forceSetBackground);
|
||||
eventSource.on(event_types.CHAT_CHANGED, moduleWorker);
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"display_name": "Chat Backgrounds",
|
||||
"loading_order": 7,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "Cohee#1207",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
#custom_bg_preview {
|
||||
width: 160px;
|
||||
height: 90px;
|
||||
background-color: var(--grey30a);
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
background-size: cover;
|
||||
border-radius: 20px;
|
||||
border: 1px solid var(--SmartThemeBorderColor);
|
||||
box-shadow: 0 0 7px var(--black50a);
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
#custom_bg_preview::before {
|
||||
content: 'No Background';
|
||||
color: white;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#custom_bg_preview:not([style*="background-image: none"])::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.background_controls .menu_button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.background_controls {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.background_controls small {
|
||||
flex-grow: 1;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"display_name": "Bulk Card Editor",
|
||||
"loading_order": 9,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "city-unit",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/city-unit"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
.bulk_select_checkbox {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
#rm_print_characters_block.bulk_select .wide100pLess70px {
|
||||
width: calc(100% - 85px);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"display_name": "CFG",
|
||||
"loading_order": 1,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "kingbri",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<a id="option_toggle_CFG">
|
||||
<i class="fa-lg fa-solid fa-scale-balanced"></i>
|
||||
<span data-i18n="CFG Scale">CFG Scale</span>
|
||||
</a>
|
|
@ -1,95 +0,0 @@
|
|||
import { chat_metadata, substituteParams, this_chid } from "../../../script.js";
|
||||
import { extension_settings, getContext } from "../../extensions.js"
|
||||
import { selected_group } from "../../group-chats.js";
|
||||
import { getCharaFilename } from "../../utils.js";
|
||||
|
||||
export const cfgType = {
|
||||
chat: 0,
|
||||
chara: 1,
|
||||
global: 2
|
||||
}
|
||||
export const metadataKeys = {
|
||||
guidance_scale: "cfg_guidance_scale",
|
||||
negative_prompt: "cfg_negative_prompt",
|
||||
positive_prompt: "cfg_positive_prompt",
|
||||
prompt_combine: "cfg_prompt_combine",
|
||||
groupchat_individual_chars: "cfg_groupchat_individual_chars",
|
||||
prompt_insertion_depth: "cfg_prompt_insertion_depth",
|
||||
prompt_separator: "cfg_prompt_separator"
|
||||
}
|
||||
|
||||
// Gets the CFG guidance scale
|
||||
// If the guidance scale is 1, ignore the CFG prompt(s) since it won't be used anyways
|
||||
export function getGuidanceScale() {
|
||||
if (!extension_settings.cfg) {
|
||||
console.warn("CFG extension is not enabled. Skipping CFG guidance.");
|
||||
return;
|
||||
}
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||
const chatGuidanceScale = chat_metadata[metadataKeys.guidance_scale];
|
||||
const groupchatCharOverride = chat_metadata[metadataKeys.groupchat_individual_chars] ?? false;
|
||||
|
||||
if (chatGuidanceScale && chatGuidanceScale !== 1 && !groupchatCharOverride) {
|
||||
return {
|
||||
type: cfgType.chat,
|
||||
value: chatGuidanceScale
|
||||
};
|
||||
}
|
||||
|
||||
if ((!selected_group && charaCfg || groupchatCharOverride) && charaCfg?.guidance_scale !== 1) {
|
||||
return {
|
||||
type: cfgType.chara,
|
||||
value: charaCfg.guidance_scale
|
||||
};
|
||||
}
|
||||
|
||||
if (extension_settings.cfg.global && extension_settings.cfg.global?.guidance_scale !== 1) {
|
||||
return {
|
||||
type: cfgType.global,
|
||||
value: extension_settings.cfg.global.guidance_scale
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the CFG prompt
|
||||
export function getCfgPrompt(guidanceScale, isNegative) {
|
||||
let splitCfgPrompt = [];
|
||||
|
||||
const cfgPromptCombine = chat_metadata[metadataKeys.prompt_combine] ?? [];
|
||||
if (guidanceScale.type === cfgType.chat || cfgPromptCombine.includes(cfgType.chat)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
chat_metadata[isNegative ? metadataKeys.negative_prompt : metadataKeys.positive_prompt]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const charaCfg = extension_settings.cfg.chara?.find((e) => e.name === getCharaFilename(this_chid));
|
||||
if (guidanceScale.type === cfgType.chara || cfgPromptCombine.includes(cfgType.chara)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
isNegative ? charaCfg.negative_prompt : charaCfg.positive_prompt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (guidanceScale.type === cfgType.global || cfgPromptCombine.includes(cfgType.global)) {
|
||||
splitCfgPrompt.unshift(
|
||||
substituteParams(
|
||||
isNegative ? extension_settings.cfg.global.negative_prompt : extension_settings.cfg.global.positive_prompt
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// This line is a bit hacky with a JSON.stringify and JSON.parse. Fix this if possible.
|
||||
const customSeparator = JSON.parse(chat_metadata[metadataKeys.prompt_separator] || JSON.stringify("\n")) ?? "\n";
|
||||
const combinedCfgPrompt = splitCfgPrompt.filter((e) => e.length > 0).join(customSeparator);
|
||||
const insertionDepth = chat_metadata[metadataKeys.prompt_insertion_depth] ?? 1;
|
||||
console.log(`Setting CFG with guidance scale: ${guidanceScale.value}, negatives: ${combinedCfgPrompt}`);
|
||||
|
||||
return {
|
||||
value: combinedCfgPrompt,
|
||||
depth: insertionDepth
|
||||
};
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
<div id="cfgConfig" class="drawer-content flexGap5">
|
||||
<div class="panelControlBar flex-container">
|
||||
<div id="cfgConfigHeader" class="fa-solid fa-grip drag-grabber"></div>
|
||||
<div id="CFGClose" class="fa-solid fa-circle-xmark"></div>
|
||||
</div>
|
||||
<div name="cfgConfigHolder" class="scrollY">
|
||||
<div id="chat_cfg_container">
|
||||
<div class="inline-drawer">
|
||||
<div id="CFGBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Chat CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small>
|
||||
<b>Unique to this chat.</b><br>
|
||||
</small>
|
||||
<label for="chat_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="chat_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="chat_cfg_guidance_scale" id="chat_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="chat_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="chat_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="chat_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="chat_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
<div id="groupchat_cfg_use_chara_container">
|
||||
<label class="checkbox_label" for="groupchat_cfg_use_chara">
|
||||
<input type="checkbox" id="groupchat_cfg_use_chara" />
|
||||
<span data-i18n="Use character CFG scales">Use character CFG scales</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chara_cfg_container" style="display: none;">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="charaANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Character CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small><b>Will be automatically added as the CFG for this character.</b></small>
|
||||
<br />
|
||||
<label for="chara_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="chara_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="chara_cfg_guidance_scale" id="chara_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="chara_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="chara_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="chara_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="chara_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="global_cfg_container">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>Global CFG</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<small><b>Will be used as the default CFG options for every chat unless overridden.</b></small>
|
||||
<br />
|
||||
<label for="global_cfg_guidance_scale">
|
||||
<span data-i18n="Scale">Scale</span>
|
||||
<small data-i18n="1 = disabled">1 = disabled</small>
|
||||
</label>
|
||||
<div class="range-block-range-and-counter">
|
||||
<div class="range-block-range">
|
||||
<input type="range" id="global_cfg_guidance_scale" name="volume" min="0.10" max="4.00" step="0.05">
|
||||
</div>
|
||||
<div class="range-block-counter">
|
||||
<div contenteditable="true" data-for="global_cfg_guidance_scale" id="global_cfg_guidance_scale_counter">
|
||||
select
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="global_cfg_negative_prompt">
|
||||
<span data-i18n="Negative Prompt">Negative Prompt</span>
|
||||
</label>
|
||||
<textarea id="global_cfg_negative_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
|
||||
<label for="global_cfg_positive_prompt">
|
||||
<span data-i18n="Positive Prompt">Positive Prompt</span>
|
||||
</label>
|
||||
<textarea id="global_cfg_positive_prompt" rows="2" class="text_pole textarea_compact" data-i18n="[placeholder]write short replies, write replies using past tense" placeholder="write short replies, write replies using past tense"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="cfg_prompt_combine_container">
|
||||
<hr class="sysHR">
|
||||
<div class="inline-drawer">
|
||||
<div id="defaultANBlockToggle" class="inline-drawer-toggle inline-drawer-header">
|
||||
<b>CFG Prompt Cascading</b>
|
||||
<div class="inline-drawer-icon fa-solid fa-circle-chevron-down down"></div>
|
||||
</div>
|
||||
<div class="inline-drawer-content">
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<small>
|
||||
<b>Combine positive/negative prompts from other boxes.</b>
|
||||
<br />
|
||||
For example, ticking the chat, global, and character boxes combine all negative prompts into a comma-separated string.
|
||||
</small>
|
||||
</div>
|
||||
<br />
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<label for="cfg_prompt_combine">
|
||||
<span data-i18n="Scale">Always Include</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="0" />
|
||||
<span data-i18n="Chat Negatives">Chat Negatives</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="1" />
|
||||
<span data-i18n="Character Negatives">Character Negatives</span>
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" name="cfg_prompt_combine" value="2" />
|
||||
<span data-i18n="Global Negatives">Global Negatives</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-container flexFlowColumn">
|
||||
<label>
|
||||
Custom Separator: <input id="cfg_prompt_separator" class="text_pole textarea_compact widthUnset" placeholder=""\n"" type="text" />
|
||||
</label>
|
||||
<label>
|
||||
Insertion Depth: <input id="cfg_prompt_insertion_depth" class="text_pole widthUnset" type="number" min="0" max="99" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -764,7 +764,7 @@ function sampleClassifyText(text) {
|
|||
// Remove asterisks and quotes
|
||||
let result = text.replace(/[\*\"]/g, '');
|
||||
|
||||
const SAMPLE_THRESHOLD = 300;
|
||||
const SAMPLE_THRESHOLD = 500;
|
||||
const HALF_SAMPLE_THRESHOLD = SAMPLE_THRESHOLD / 2;
|
||||
|
||||
if (text.length < SAMPLE_THRESHOLD) {
|
||||
|
@ -1498,6 +1498,8 @@ function setExpressionOverrideHtml(forceClear = false) {
|
|||
if (isVisualNovelMode()) {
|
||||
$('#visual-novel-wrapper').empty();
|
||||
}
|
||||
|
||||
updateFunction();
|
||||
});
|
||||
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
|
||||
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"display_name": "Memory",
|
||||
"display_name": "Summarize",
|
||||
"loading_order": 9,
|
||||
"requires": [],
|
||||
"optional": [
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { saveSettingsDebounced, callPopup, getRequestHeaders, substituteParams } from "../../../script.js";
|
||||
import { getContext, extension_settings } from "../../extensions.js";
|
||||
import { initScrollHeight, resetScrollHeight } from "../../utils.js";
|
||||
import { executeSlashCommands, getSlashCommandsHelp, registerSlashCommand } from "../../slash-commands.js";
|
||||
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
|
||||
export { MODULE_NAME };
|
||||
|
||||
|
@ -15,8 +14,9 @@ const defaultSettings = {
|
|||
quickReplyEnabled: false,
|
||||
numberOfSlots: 5,
|
||||
quickReplySlots: [],
|
||||
placeBeforePromptEnabled: false,
|
||||
placeBeforeInputEnabled: false,
|
||||
quickActionEnabled: false,
|
||||
AutoInputInject: true,
|
||||
}
|
||||
|
||||
//method from worldinfo
|
||||
|
@ -35,8 +35,12 @@ async function updateQuickReplyPresetList() {
|
|||
|
||||
|
||||
if (presets !== undefined) {
|
||||
presets.forEach((item, i) => {
|
||||
$("#quickReplyPresets").append(`<option value='${item.name}'${selected_preset.includes(item.name) ? ' selected' : ''}>${item.name}</option>`);
|
||||
presets.forEach((item) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = item.name;
|
||||
option.innerText = item.name;
|
||||
option.selected = selected_preset.includes(item.name);
|
||||
$("#quickReplyPresets").append(option);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +54,10 @@ async function loadSettings(type) {
|
|||
Object.assign(extension_settings.quickReply, defaultSettings);
|
||||
}
|
||||
|
||||
if (extension_settings.quickReply.AutoInputInject === undefined) {
|
||||
extension_settings.quickReply.AutoInputInject = true;
|
||||
}
|
||||
|
||||
// If the user has an old version of the extension, update it
|
||||
if (!Array.isArray(extension_settings.quickReply.quickReplySlots)) {
|
||||
extension_settings.quickReply.quickReplySlots = [];
|
||||
|
@ -77,20 +85,21 @@ async function loadSettings(type) {
|
|||
|
||||
$('#quickReplyEnabled').prop('checked', extension_settings.quickReply.quickReplyEnabled);
|
||||
$('#quickReplyNumberOfSlots').val(extension_settings.quickReply.numberOfSlots);
|
||||
$('#placeBeforePromptEnabled').prop('checked', extension_settings.quickReply.placeBeforePromptEnabled);
|
||||
$('#placeBeforeInputEnabled').prop('checked', extension_settings.quickReply.placeBeforeInputEnabled);
|
||||
$('#quickActionEnabled').prop('checked', extension_settings.quickReply.quickActionEnabled);
|
||||
$('#AutoInputInject').prop('checked', extension_settings.quickReply.AutoInputInject);
|
||||
}
|
||||
|
||||
function onQuickReplyInput(id) {
|
||||
extension_settings.quickReply.quickReplySlots[id - 1].mes = $(`#quickReply${id}Mes`).val();
|
||||
$(`#quickReply${id}`).attr('title', ($(`#quickReply${id}Mes`).val()));
|
||||
$(`#quickReply${id}`).attr('title', String($(`#quickReply${id}Mes`).val()));
|
||||
resetScrollHeight($(`#quickReply${id}Mes`));
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onQuickReplyLabelInput(id) {
|
||||
extension_settings.quickReply.quickReplySlots[id - 1].label = $(`#quickReply${id}Label`).val();
|
||||
$(`#quickReply${id}`).text($(`#quickReply${id}Label`).val());
|
||||
$(`#quickReply${id}`).text(String($(`#quickReply${id}Label`).val()));
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
|
@ -109,8 +118,13 @@ async function onQuickActionEnabledInput() {
|
|||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onPlaceBeforePromptEnabledInput() {
|
||||
extension_settings.quickReply.placeBeforePromptEnabled = !!$(this).prop('checked');
|
||||
async function onPlaceBeforeInputEnabledInput() {
|
||||
extension_settings.quickReply.placeBeforeInputEnabled = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onAutoInputInject() {
|
||||
extension_settings.quickReply.AutoInputInject = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
|
@ -125,16 +139,15 @@ async function sendQuickReply(index) {
|
|||
|
||||
let newText;
|
||||
|
||||
if (existingText) {
|
||||
// If existing text, add space after prompt
|
||||
if (extension_settings.quickReply.placeBeforePromptEnabled) {
|
||||
if (existingText && extension_settings.quickReply.AutoInputInject) {
|
||||
if (extension_settings.quickReply.placeBeforeInputEnabled) {
|
||||
newText = `${prompt} ${existingText} `;
|
||||
} else {
|
||||
newText = `${existingText} ${prompt} `;
|
||||
}
|
||||
} else {
|
||||
// If no existing text, add prompt only (with a trailing space)
|
||||
newText = prompt + ' ';
|
||||
// If no existing text and placeBeforeInputEnabled false, add prompt only (with a trailing space)
|
||||
newText = `${prompt} `;
|
||||
}
|
||||
|
||||
newText = substituteParams(newText);
|
||||
|
@ -142,9 +155,9 @@ async function sendQuickReply(index) {
|
|||
$("#send_textarea").val(newText);
|
||||
|
||||
// Set the focus back to the textarea
|
||||
$("#send_textarea").focus();
|
||||
$("#send_textarea").trigger('focus');
|
||||
|
||||
// Only trigger send button if quickActionEnabled is not checked or
|
||||
// Only trigger send button if quickActionEnabled is not checked or
|
||||
// the prompt starts with '/'
|
||||
if (!extension_settings.quickReply.quickActionEnabled || prompt.startsWith('/')) {
|
||||
$("#send_but").trigger('click');
|
||||
|
@ -221,7 +234,7 @@ async function saveQuickReplyPreset() {
|
|||
}
|
||||
else {
|
||||
presets[quickReplyPresetIndex] = quickReplyPreset;
|
||||
$(`#quickReplyPresets option[value="${name}"]`).attr('selected', true);
|
||||
$(`#quickReplyPresets option[value="${name}"]`).prop('selected', true);
|
||||
}
|
||||
saveSettingsDebounced();
|
||||
} else {
|
||||
|
@ -274,8 +287,8 @@ function generateQuickReplyElements() {
|
|||
for (let i = 1; i <= extension_settings.quickReply.numberOfSlots; i++) {
|
||||
quickReplyHtml += `
|
||||
<div class="flex-container alignitemsflexstart">
|
||||
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Add a button label)">
|
||||
<textarea id="quickReply${i}Mes" placeholder="(custom message here)" class="text_pole widthUnset flex1" rows="2"></textarea>
|
||||
<input class="text_pole wide30p" id="quickReply${i}Label" placeholder="(Button label)">
|
||||
<textarea id="quickReply${i}Mes" placeholder="(Custom message or /command)" class="text_pole widthUnset flex1" rows="2"></textarea>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@ -309,7 +322,7 @@ async function applyQuickReplyPreset(name) {
|
|||
addQuickReplyBar();
|
||||
moduleWorker();
|
||||
|
||||
$(`#quickReplyPresets option[value="${name}"]`).attr('selected', true);
|
||||
$(`#quickReplyPresets option[value="${name}"]`).prop('selected', true);
|
||||
console.debug('QR Preset applied: ' + name);
|
||||
}
|
||||
|
||||
|
@ -334,7 +347,6 @@ async function doQR(_, text) {
|
|||
}
|
||||
|
||||
jQuery(async () => {
|
||||
|
||||
moduleWorker();
|
||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||
const settingsHtml = `
|
||||
|
@ -348,20 +360,28 @@ jQuery(async () => {
|
|||
<div>
|
||||
<label class="checkbox_label">
|
||||
<input id="quickReplyEnabled" type="checkbox" />
|
||||
Enable Quick Replies
|
||||
Enable Quick Replies
|
||||
</label>
|
||||
<label class="checkbox_label">
|
||||
<input id="quickActionEnabled" type="checkbox" />
|
||||
Disable Send / Insert In User Input
|
||||
Disable Send / Insert In User Input
|
||||
</label>
|
||||
<label class="checkbox_label marginBot10">
|
||||
<input id="placeBeforePromptEnabled" type="checkbox" />
|
||||
Place Quick-reply before the Prompt
|
||||
<input id="placeBeforeInputEnabled" type="checkbox" />
|
||||
Place Quick-reply before the Input
|
||||
</label>
|
||||
<label class="checkbox_label marginBot10">
|
||||
<input id="AutoInputInject" type="checkbox" />
|
||||
Inject user input automatically<br>(If disabled, use {{input}} macro for manual injection)
|
||||
</label>
|
||||
<label for="quickReplyPresets">Quick Reply presets:</label>
|
||||
<div class="flex-container flexnowrap wide100p">
|
||||
<select id="quickReplyPresets" name="quickreply-preset">
|
||||
<select id="quickReplyPresets" name="quickreply-preset" class="flex1 text_pole">
|
||||
</select>
|
||||
<i id="quickReplyPresetSaveButton" class="fa-solid fa-save"></i>
|
||||
<div id="quickReplyPresetSaveButton" class="menu_button menu_button_icon">
|
||||
<div class="fa-solid fa-save"></div>
|
||||
<span>Save</span>
|
||||
</div>
|
||||
</div>
|
||||
<label for="quickReplyNumberOfSlots">Number of slots:</label>
|
||||
</div>
|
||||
|
@ -379,10 +399,11 @@ jQuery(async () => {
|
|||
</div>`;
|
||||
|
||||
$('#extensions_settings2').append(settingsHtml);
|
||||
|
||||
|
||||
// Add event handler for quickActionEnabled
|
||||
$('#quickActionEnabled').on('input', onQuickActionEnabledInput);
|
||||
$('#placeBeforePromptEnabled').on('input', onPlaceBeforePromptEnabledInput);
|
||||
$('#placeBeforeInputEnabled').on('input', onPlaceBeforeInputEnabledInput);
|
||||
$('#AutoInputInject').on('input', onAutoInputInject);
|
||||
$('#quickReplyEnabled').on('input', onQuickReplyEnabledInput);
|
||||
$('#quickReplyNumberOfSlotsApply').on('click', onQuickReplyNumberOfSlotsInput);
|
||||
$("#quickReplyPresetSaveButton").on('click', saveQuickReplyPreset);
|
||||
|
@ -392,16 +413,13 @@ jQuery(async () => {
|
|||
extension_settings.quickReplyPreset = quickReplyPresetSelected;
|
||||
applyQuickReplyPreset(quickReplyPresetSelected);
|
||||
saveSettingsDebounced();
|
||||
|
||||
});
|
||||
|
||||
await loadSettings('init');
|
||||
addQuickReplyBar();
|
||||
|
||||
});
|
||||
|
||||
$(document).ready(() => {
|
||||
jQuery(() => {
|
||||
registerSlashCommand('qr', doQR, [], '<span class="monospace">(number)</span> – activates the specified Quick Reply', true, true);
|
||||
registerSlashCommand('qrset', doQRPresetSwitch, [], '<span class="monospace">(name)</span> – swaps to the specified Quick Reply Preset', true, true);
|
||||
|
||||
})
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"display_name": "Settings Search",
|
||||
"loading_order": 15,
|
||||
"requires": [],
|
||||
"optional": [],
|
||||
"js": "index.js",
|
||||
"css": "style.css",
|
||||
"author": "RossAscends",
|
||||
"version": "1.0.0",
|
||||
"homePage": "https://github.com/SillyTavern/SillyTavern"
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
.highlighted {
|
||||
color: black;
|
||||
background-color: yellow;
|
||||
text-shadow: none !important;
|
||||
}
|
|
@ -121,6 +121,17 @@ const helpString = [
|
|||
example: '/sd apple tree' would generate a picture of an apple tree.`,
|
||||
].join('<br>');
|
||||
|
||||
const defaultPrefix = 'best quality, absurdres, aesthetic,';
|
||||
const defaultNegative = 'lowres, bad anatomy, bad hands, text, error, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry';
|
||||
|
||||
const defaultStyles = [
|
||||
{
|
||||
name: 'Default',
|
||||
negative: defaultNegative,
|
||||
prefix: defaultPrefix,
|
||||
},
|
||||
];
|
||||
|
||||
const defaultSettings = {
|
||||
source: sources.extras,
|
||||
|
||||
|
@ -143,8 +154,8 @@ const defaultSettings = {
|
|||
width: 512,
|
||||
height: 512,
|
||||
|
||||
prompt_prefix: 'best quality, absurdres, masterpiece,',
|
||||
negative_prompt: 'lowres, bad anatomy, bad hands, text, error, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry',
|
||||
prompt_prefix: defaultPrefix,
|
||||
negative_prompt: defaultNegative,
|
||||
sampler: 'DDIM',
|
||||
model: '',
|
||||
|
||||
|
@ -160,6 +171,7 @@ const defaultSettings = {
|
|||
|
||||
// Refine mode
|
||||
refine_mode: false,
|
||||
expand: false,
|
||||
|
||||
prompts: promptTemplates,
|
||||
|
||||
|
@ -190,6 +202,9 @@ const defaultSettings = {
|
|||
novel_upscale_ratio_step: 0.1,
|
||||
novel_upscale_ratio: 1.0,
|
||||
novel_anlas_guard: false,
|
||||
|
||||
style: 'Default',
|
||||
styles: defaultStyles,
|
||||
}
|
||||
|
||||
function getSdRequestBody() {
|
||||
|
@ -238,6 +253,10 @@ async function loadSettings() {
|
|||
extension_settings.sd.character_prompts = {};
|
||||
}
|
||||
|
||||
if (!Array.isArray(extension_settings.sd.styles)) {
|
||||
extension_settings.sd.styles = defaultStyles;
|
||||
}
|
||||
|
||||
$('#sd_source').val(extension_settings.sd.source);
|
||||
$('#sd_scale').val(extension_settings.sd.scale).trigger('input');
|
||||
$('#sd_steps').val(extension_settings.sd.steps).trigger('input');
|
||||
|
@ -257,11 +276,20 @@ async function loadSettings() {
|
|||
$('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces);
|
||||
$('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr);
|
||||
$('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode);
|
||||
$('#sd_expand').prop('checked', extension_settings.sd.expand);
|
||||
$('#sd_auto_url').val(extension_settings.sd.auto_url);
|
||||
$('#sd_auto_auth').val(extension_settings.sd.auto_auth);
|
||||
$('#sd_vlad_url').val(extension_settings.sd.vlad_url);
|
||||
$('#sd_vlad_auth').val(extension_settings.sd.vlad_auth);
|
||||
|
||||
for (const style of extension_settings.sd.styles) {
|
||||
const option = document.createElement('option');
|
||||
option.value = style.name;
|
||||
option.text = style.name;
|
||||
option.selected = style.name === extension_settings.sd.style;
|
||||
$('#sd_style').append(option);
|
||||
}
|
||||
|
||||
toggleSourceControls();
|
||||
addPromptTemplates();
|
||||
|
||||
|
@ -300,7 +328,88 @@ function addPromptTemplates() {
|
|||
}
|
||||
}
|
||||
|
||||
async function refinePrompt(prompt) {
|
||||
function onStyleSelect() {
|
||||
const selectedStyle = String($('#sd_style').find(':selected').val());
|
||||
const styleObject = extension_settings.sd.styles.find(x => x.name === selectedStyle);
|
||||
|
||||
if (!styleObject) {
|
||||
console.warn(`Could not find style object for ${selectedStyle}`);
|
||||
return;
|
||||
}
|
||||
|
||||
$('#sd_prompt_prefix').val(styleObject.prefix).trigger('input');
|
||||
$('#sd_negative_prompt').val(styleObject.negative).trigger('input');
|
||||
extension_settings.sd.style = selectedStyle;
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function onSaveStyleClick() {
|
||||
const userInput = await callPopup('Enter style name:', 'input', '', { okButton: 'Save' });
|
||||
|
||||
if (!userInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = String(userInput).trim();
|
||||
const prefix = String($('#sd_prompt_prefix').val());
|
||||
const negative = String($('#sd_negative_prompt').val());
|
||||
|
||||
const existingStyle = extension_settings.sd.styles.find(x => x.name === name);
|
||||
|
||||
if (existingStyle) {
|
||||
existingStyle.prefix = prefix;
|
||||
existingStyle.negative = negative;
|
||||
$('#sd_style').val(name);
|
||||
saveSettingsDebounced();
|
||||
return;
|
||||
}
|
||||
|
||||
const styleObject = {
|
||||
name: name,
|
||||
prefix: prefix,
|
||||
negative: negative,
|
||||
};
|
||||
|
||||
extension_settings.sd.styles.push(styleObject);
|
||||
const option = document.createElement('option');
|
||||
option.value = styleObject.name;
|
||||
option.text = styleObject.name;
|
||||
option.selected = true;
|
||||
$('#sd_style').append(option);
|
||||
$('#sd_style').val(styleObject.name);
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
async function expandPrompt(prompt) {
|
||||
try {
|
||||
const response = await fetch('/api/sd/expand', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ prompt: prompt }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('API returned an error.');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data.prompt;
|
||||
} catch {
|
||||
return prompt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies prompt based on auto-expansion and user inputs.
|
||||
* @param {string} prompt Prompt to refine
|
||||
* @param {boolean} allowExpand Whether to allow auto-expansion
|
||||
* @returns {Promise<string>} Refined prompt
|
||||
*/
|
||||
async function refinePrompt(prompt, allowExpand) {
|
||||
if (allowExpand && extension_settings.sd.expand) {
|
||||
prompt = await expandPrompt(prompt);
|
||||
}
|
||||
|
||||
if (extension_settings.sd.refine_mode) {
|
||||
const refinedPrompt = await callPopup('<h3>Review and edit the prompt:</h3>Press "Cancel" to abort the image generation.', 'input', prompt.trim(), { rows: 5, okButton: 'Generate' });
|
||||
|
||||
|
@ -346,7 +455,14 @@ function getCharacterPrefix() {
|
|||
return '';
|
||||
}
|
||||
|
||||
function combinePrefixes(str1, str2) {
|
||||
/**
|
||||
* Combines two prompt prefixes into one.
|
||||
* @param {string} str1 Base string
|
||||
* @param {string} str2 Secondary string
|
||||
* @param {string} macro Macro to replace with the secondary string
|
||||
* @returns {string} Combined string with a comma between them
|
||||
*/
|
||||
function combinePrefixes(str1, str2, macro = '') {
|
||||
if (!str2) {
|
||||
return str1;
|
||||
}
|
||||
|
@ -355,12 +471,16 @@ function combinePrefixes(str1, str2) {
|
|||
str1 = str1.trim().replace(/^,|,$/g, '');
|
||||
str2 = str2.trim().replace(/^,|,$/g, '');
|
||||
|
||||
// Combine the strings with a comma between them
|
||||
var result = `${str1}, ${str2},`;
|
||||
|
||||
// Combine the strings with a comma between them)
|
||||
const result = macro && str1.includes(macro) ? str1.replace(macro, str2) : `${str1}, ${str2},`;
|
||||
return result;
|
||||
}
|
||||
|
||||
function onExpandInput() {
|
||||
extension_settings.sd.expand = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function onRefineModeInput() {
|
||||
extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
@ -961,17 +1081,21 @@ async function loadNovelModels() {
|
|||
}
|
||||
|
||||
return [
|
||||
{
|
||||
value: 'nai-diffusion-2',
|
||||
text: 'NAI Diffusion Anime V2',
|
||||
},
|
||||
{
|
||||
value: 'nai-diffusion',
|
||||
text: 'Full',
|
||||
text: 'NAI Diffusion Anime V1 (Full)',
|
||||
},
|
||||
{
|
||||
value: 'safe-diffusion',
|
||||
text: 'Safe',
|
||||
text: 'NAI Diffusion Anime V1 (Curated)',
|
||||
},
|
||||
{
|
||||
value: 'nai-diffusion-furry',
|
||||
text: 'Furry',
|
||||
text: 'NAI Diffusion Furry',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -1080,10 +1204,9 @@ async function generatePicture(_, trigger, message, callback) {
|
|||
extension_settings.sd.width = Math.round(extension_settings.sd.height * 1.8 / 64) * 64;
|
||||
}
|
||||
const callbackOriginal = callback;
|
||||
callback = async function (prompt, base64Image) {
|
||||
const imagePath = base64Image;
|
||||
const imgUrl = `url("${encodeURI(base64Image)}")`;
|
||||
eventSource.emit(event_types.FORCE_SET_BACKGROUND, imgUrl);
|
||||
callback = async function (prompt, imagePath) {
|
||||
const imgUrl = `url("${encodeURI(imagePath)}")`;
|
||||
eventSource.emit(event_types.FORCE_SET_BACKGROUND, { url: imgUrl, path: imagePath });
|
||||
|
||||
if (typeof callbackOriginal === 'function') {
|
||||
callbackOriginal(prompt, imagePath);
|
||||
|
@ -1129,14 +1252,14 @@ async function getPrompt(generationType, message, trigger, quiet_prompt) {
|
|||
}
|
||||
|
||||
if (generationType !== generationMode.FREE) {
|
||||
prompt = await refinePrompt(prompt);
|
||||
prompt = await refinePrompt(prompt, true);
|
||||
}
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
async function generatePrompt(quiet_prompt) {
|
||||
const reply = await generateQuietPrompt(quiet_prompt, false);
|
||||
const reply = await generateQuietPrompt(quiet_prompt, false, false);
|
||||
return processReply(reply);
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1268,7 @@ async function sendGenerationRequest(generationType, prompt, characterName = nul
|
|||
? combinePrefixes(extension_settings.sd.prompt_prefix, getCharacterPrefix())
|
||||
: extension_settings.sd.prompt_prefix;
|
||||
|
||||
const prefixedPrompt = combinePrefixes(prefix, prompt);
|
||||
const prefixedPrompt = combinePrefixes(prefix, prompt, '{prompt}');
|
||||
|
||||
let result = { format: '', data: '' };
|
||||
const currentChatId = getCurrentChatId();
|
||||
|
@ -1345,13 +1468,13 @@ function getNovelParams() {
|
|||
let width = extension_settings.sd.width;
|
||||
let height = extension_settings.sd.height;
|
||||
|
||||
// Don't apply Anlas guard if it's disabled.d
|
||||
// Don't apply Anlas guard if it's disabled.
|
||||
if (!extension_settings.sd.novel_anlas_guard) {
|
||||
return { steps, width, height };
|
||||
}
|
||||
|
||||
const MAX_STEPS = 28;
|
||||
const MAX_PIXELS = 409600;
|
||||
const MAX_PIXELS = 1024 * 1024;
|
||||
|
||||
if (width * height > MAX_PIXELS) {
|
||||
const ratio = Math.sqrt(MAX_PIXELS / (width * height));
|
||||
|
@ -1523,7 +1646,7 @@ async function sdMessageButton(e) {
|
|||
try {
|
||||
setBusyIcon(true);
|
||||
if (hasSavedImage) {
|
||||
const prompt = await refinePrompt(message.extra.title);
|
||||
const prompt = await refinePrompt(message.extra.title, false);
|
||||
message.extra.title = prompt;
|
||||
|
||||
console.log('Regenerating an image, using existing prompt:', prompt);
|
||||
|
@ -1610,6 +1733,9 @@ jQuery(async () => {
|
|||
$('#sd_novel_upscale_ratio').on('input', onNovelUpscaleRatioInput);
|
||||
$('#sd_novel_anlas_guard').on('input', onNovelAnlasGuardInput);
|
||||
$('#sd_novel_view_anlas').on('click', onViewAnlasClick);
|
||||
$('#sd_expand').on('input', onExpandInput);
|
||||
$('#sd_style').on('change', onStyleSelect);
|
||||
$('#sd_save_style').on('click', onSaveStyleClick);
|
||||
$('#sd_character_prompt_block').hide();
|
||||
|
||||
$('.sd_settings .inline-drawer-toggle').on('click', function () {
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
<input id="sd_refine_mode" type="checkbox" />
|
||||
Edit prompts before generation
|
||||
</label>
|
||||
<label for="sd_expand" class="checkbox_label" title="Automatically extend prompts using text generation model">
|
||||
<input id="sd_expand" type="checkbox" />
|
||||
Auto-enhance prompts
|
||||
</label>
|
||||
<label for="sd_source">Source</label>
|
||||
<select id="sd_source">
|
||||
<option value="extras">Extras API (local / remote)</option>
|
||||
|
@ -122,15 +126,25 @@
|
|||
<label for="sd_novel_upscale_ratio">Upscale by (<span id="sd_novel_upscale_ratio_value"></span>)</label>
|
||||
<input id="sd_novel_upscale_ratio" type="range" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" />
|
||||
</div>
|
||||
<hr>
|
||||
<h4 title="Preset for prompt prefix and negative prompt">
|
||||
Style
|
||||
</h4>
|
||||
<div class="flex-container">
|
||||
<select id="sd_style" class="flex1 text_pole"></select>
|
||||
<div id="sd_save_style" title="Save style" class="menu_button">
|
||||
<i class="fa-solid fa-save"></i>
|
||||
</div>
|
||||
</div>
|
||||
<label for="sd_prompt_prefix">Common prompt prefix</label>
|
||||
<textarea id="sd_prompt_prefix" class="text_pole textarea_compact" rows="3"></textarea>
|
||||
<textarea id="sd_prompt_prefix" class="text_pole textarea_compact" rows="3" placeholder="Use {prompt} to specify where the generated prompt will be inserted"></textarea>
|
||||
<label for="sd_negative_prompt">Negative prompt</label>
|
||||
<textarea id="sd_negative_prompt" class="text_pole textarea_compact" rows="3"></textarea>
|
||||
<div id="sd_character_prompt_block">
|
||||
<label for="sd_character_prompt">Character-specific prompt prefix</label>
|
||||
<small>Won't be used in groups.</small>
|
||||
<textarea id="sd_character_prompt" class="text_pole textarea_compact" rows="3" placeholder="Any characteristics that describe the currently selected character. Will be added after a common prefix. Example: female, green eyes, brown hair, pink shirt"></textarea>
|
||||
</div>
|
||||
<label for="sd_negative_prompt">Negative prompt</label>
|
||||
<textarea id="sd_negative_prompt" class="text_pole textarea_compact" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline-drawer">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { callPopup, main_api } from "../../../script.js";
|
||||
import { getContext } from "../../extensions.js";
|
||||
import { getTokenizerModel } from "../../tokenizers.js";
|
||||
import { registerSlashCommand } from "../../slash-commands.js";
|
||||
import { getTokenCount, getTokenizerModel } from "../../tokenizers.js";
|
||||
|
||||
async function doTokenCounter() {
|
||||
const selectedTokenizer = main_api == 'openai'
|
||||
|
@ -29,6 +30,20 @@ async function doTokenCounter() {
|
|||
callPopup(dialog, 'text');
|
||||
}
|
||||
|
||||
function doCount() {
|
||||
// get all of the messages in the chat
|
||||
const context = getContext();
|
||||
const messages = context.chat.filter(x => x.mes && !x.is_system).map(x => x.mes);
|
||||
|
||||
//concat all the messages into a single string
|
||||
const allMessages = messages.join(' ');
|
||||
|
||||
console.debug('All messages:', allMessages);
|
||||
|
||||
//toastr success with the token count of the chat
|
||||
toastr.success(`Token count: ${getTokenCount(allMessages)}`);
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
const buttonHtml = `
|
||||
<div id="token_counter" class="list-group-item flex-container flexGap5">
|
||||
|
@ -37,4 +52,5 @@ jQuery(() => {
|
|||
</div>`;
|
||||
$('#extensionsMenu').prepend(buttonHtml);
|
||||
$('#token_counter').on('click', doTokenCounter);
|
||||
registerSlashCommand('count', doCount, [], '– counts the number of tokens in the current chat', true, false);
|
||||
});
|
||||
|
|
|
@ -10,15 +10,12 @@ class ElevenLabsTtsProvider {
|
|||
voices = []
|
||||
separator = ' ... ... ... '
|
||||
|
||||
get settings() {
|
||||
return this.settings
|
||||
}
|
||||
|
||||
defaultSettings = {
|
||||
stability: 0.75,
|
||||
similarity_boost: 0.75,
|
||||
apiKey: "",
|
||||
multilingual: false,
|
||||
model: 'eleven_monolingual_v1',
|
||||
voiceMap: {}
|
||||
}
|
||||
|
||||
|
@ -27,15 +24,17 @@ class ElevenLabsTtsProvider {
|
|||
<div class="elevenlabs_tts_settings">
|
||||
<label for="elevenlabs_tts_api_key">API Key</label>
|
||||
<input id="elevenlabs_tts_api_key" type="text" class="text_pole" placeholder="<API Key>"/>
|
||||
<label for="elevenlabs_tts_model">Model</label>
|
||||
<select id="elevenlabs_tts_model" class="text_pole">
|
||||
<option value="eleven_monolingual_v1">Monolingual</option>
|
||||
<option value="eleven_multilingual_v1">Multilingual v1</option>
|
||||
<option value="eleven_multilingual_v2">Multilingual v2</option>
|
||||
</select>
|
||||
<input id="eleven_labs_connect" class="menu_button" type="button" value="Connect" />
|
||||
<label for="elevenlabs_tts_stability">Stability: <span id="elevenlabs_tts_stability_output"></span></label>
|
||||
<input id="elevenlabs_tts_stability" type="range" value="${this.defaultSettings.stability}" min="0" max="1" step="0.05" />
|
||||
<label for="elevenlabs_tts_similarity_boost">Similarity Boost: <span id="elevenlabs_tts_similarity_boost_output"></span></label>
|
||||
<input id="elevenlabs_tts_similarity_boost" type="range" value="${this.defaultSettings.similarity_boost}" min="0" max="1" step="0.05" />
|
||||
<label class="checkbox_label" for="elevenlabs_tts_multilingual">
|
||||
<input id="elevenlabs_tts_multilingual" type="checkbox" value="${this.defaultSettings.multilingual}" />
|
||||
Enable Multilingual
|
||||
</label>
|
||||
</div>
|
||||
`
|
||||
return html
|
||||
|
@ -45,11 +44,10 @@ class ElevenLabsTtsProvider {
|
|||
// Update dynamically
|
||||
this.settings.stability = $('#elevenlabs_tts_stability').val()
|
||||
this.settings.similarity_boost = $('#elevenlabs_tts_similarity_boost').val()
|
||||
this.settings.multilingual = $('#elevenlabs_tts_multilingual').prop('checked')
|
||||
this.settings.model = $('#elevenlabs_tts_model').find(':selected').val()
|
||||
saveTtsProviderSettings()
|
||||
}
|
||||
|
||||
|
||||
async loadSettings(settings) {
|
||||
// Pupulate Provider UI given input settings
|
||||
if (Object.keys(settings).length == 0) {
|
||||
|
@ -59,26 +57,39 @@ class ElevenLabsTtsProvider {
|
|||
// Only accept keys defined in defaultSettings
|
||||
this.settings = this.defaultSettings
|
||||
|
||||
for (const key in settings){
|
||||
if (key in this.settings){
|
||||
// Migrate old settings
|
||||
if (settings['multilingual'] !== undefined) {
|
||||
settings.model = settings.multilingual ? 'eleven_multilingual_v1' : 'eleven_monolingual_v1';
|
||||
delete settings['multilingual'];
|
||||
}
|
||||
|
||||
for (const key in settings) {
|
||||
if (key in this.settings) {
|
||||
this.settings[key] = settings[key]
|
||||
} else {
|
||||
throw `Invalid setting passed to TTS Provider: ${key}`
|
||||
}
|
||||
}
|
||||
|
||||
$('#elevenlabs_tts_stability').val(this.settings.stability)
|
||||
$('#elevenlabs_tts_similarity_boost').val(this.settings.similarity_boost)
|
||||
$('#elevenlabs_tts_api_key').val(this.settings.apiKey)
|
||||
$('#tts_auto_generation').prop('checked', this.settings.multilingual)
|
||||
$('#eleven_labs_connect').on('click', () => {this.onConnectClick()})
|
||||
$('#elevenlabs_tts_settings').on('input',this.onSettingsChange)
|
||||
$('#elevenlabs_tts_model').val(this.settings.model);
|
||||
$('#eleven_labs_connect').on('click', () => { this.onConnectClick() })
|
||||
$('#elevenlabs_tts_similarity_boost').on('input', this.onSettingsChange.bind(this))
|
||||
$('#elevenlabs_tts_stability').on('input', this.onSettingsChange.bind(this))
|
||||
$('#elevenlabs_tts_model').on('change', this.onSettingsChange.bind(this))
|
||||
|
||||
await this.checkReady()
|
||||
console.debug("ElevenLabs: Settings loaded")
|
||||
try {
|
||||
await this.checkReady()
|
||||
console.debug("ElevenLabs: Settings loaded")
|
||||
} catch {
|
||||
console.debug("ElevenLabs: Settings loaded, but not ready")
|
||||
}
|
||||
}
|
||||
|
||||
// Perform a simple readiness check by trying to fetch voiceIds
|
||||
async checkReady(){
|
||||
async checkReady() {
|
||||
await this.fetchTtsVoiceObjects()
|
||||
}
|
||||
|
||||
|
@ -87,7 +98,7 @@ class ElevenLabsTtsProvider {
|
|||
|
||||
async onConnectClick() {
|
||||
// Update on Apply click
|
||||
return await this.updateApiKey().catch( (error) => {
|
||||
return await this.updateApiKey().catch((error) => {
|
||||
toastr.error(`ElevenLabs: ${error}`)
|
||||
})
|
||||
}
|
||||
|
@ -102,6 +113,7 @@ class ElevenLabsTtsProvider {
|
|||
})
|
||||
this.settings.apiKey = this.settings.apiKey
|
||||
console.debug(`Saved new API_KEY: ${this.settings.apiKey}`)
|
||||
$('#tts_status').text('')
|
||||
this.onSettingsChange()
|
||||
}
|
||||
|
||||
|
@ -123,7 +135,7 @@ class ElevenLabsTtsProvider {
|
|||
}
|
||||
|
||||
|
||||
async generateTts(text, voiceId){
|
||||
async generateTts(text, voiceId) {
|
||||
const historyId = await this.findTtsGenerationInHistory(text, voiceId)
|
||||
|
||||
let response
|
||||
|
@ -189,11 +201,8 @@ class ElevenLabsTtsProvider {
|
|||
}
|
||||
|
||||
async fetchTtsGeneration(text, voiceId) {
|
||||
let model = "eleven_monolingual_v1"
|
||||
if (this.settings.multilingual == true) {
|
||||
model = "eleven_multilingual_v1"
|
||||
}
|
||||
console.info(`Generating new TTS for voice_id ${voiceId}`)
|
||||
let model = this.settings.model ?? "eleven_monolingual_v1";
|
||||
console.info(`Generating new TTS for voice_id ${voiceId}, model ${model}`)
|
||||
const response = await fetch(
|
||||
`https://api.elevenlabs.io/v1/text-to-speech/${voiceId}`,
|
||||
{
|
||||
|
@ -203,9 +212,12 @@ class ElevenLabsTtsProvider {
|
|||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: model,
|
||||
model_id: model,
|
||||
text: text,
|
||||
voice_settings: this.settings
|
||||
voice_settings: {
|
||||
stability: Number(this.settings.stability),
|
||||
similarity_boost: Number(this.settings.similarity_boost),
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
@ -429,7 +429,7 @@ async function processTtsQueue() {
|
|||
|
||||
console.debug('New message found, running TTS')
|
||||
currentTtsJob = ttsJobQueue.shift()
|
||||
let text = extension_settings.tts.narrate_translated_only ? currentTtsJob?.extra?.display_text : currentTtsJob.mes
|
||||
let text = extension_settings.tts.narrate_translated_only ? (currentTtsJob?.extra?.display_text || currentTtsJob.mes) : currentTtsJob.mes
|
||||
text = extension_settings.tts.narrate_dialogues_only
|
||||
? text.replace(/\*[^\*]*?(\*|$)/g, '').trim() // remove asterisks content
|
||||
: text.replaceAll('*', '').trim() // remove just the asterisks
|
||||
|
@ -614,8 +614,8 @@ function onTtsProviderChange() {
|
|||
|
||||
// Ensure that TTS provider settings are saved to extension settings.
|
||||
export function saveTtsProviderSettings() {
|
||||
updateVoiceMap()
|
||||
extension_settings.tts[ttsProviderName] = ttsProvider.settings
|
||||
updateVoiceMap()
|
||||
saveSettingsDebounced()
|
||||
console.info(`Saved settings ${ttsProviderName} ${JSON.stringify(ttsProvider.settings)}`)
|
||||
}
|
||||
|
@ -695,6 +695,9 @@ function updateVoiceMap() {
|
|||
voiceMap = tempVoiceMap
|
||||
console.log(`Voicemap updated to ${JSON.stringify(voiceMap)}`)
|
||||
}
|
||||
if (!extension_settings.tts[ttsProviderName].voiceMap) {
|
||||
extension_settings.tts[ttsProviderName].voiceMap = {}
|
||||
}
|
||||
Object.assign(extension_settings.tts[ttsProviderName].voiceMap, voiceMap)
|
||||
saveSettingsDebounced()
|
||||
}
|
||||
|
@ -748,10 +751,6 @@ class VoiceMapEntry {
|
|||
*
|
||||
*/
|
||||
export async function initVoiceMap(){
|
||||
// Clear existing voiceMap state
|
||||
$('#tts_voicemap_block').empty()
|
||||
voiceMapEntries = []
|
||||
|
||||
// Gate initialization if not enabled or TTS Provider not ready. Prevents error popups.
|
||||
const enabled = $('#tts_enabled').is(':checked')
|
||||
if (!enabled){
|
||||
|
@ -769,6 +768,10 @@ export async function initVoiceMap(){
|
|||
|
||||
setTtsStatus("TTS Provider Loaded", true)
|
||||
|
||||
// Clear existing voiceMap state
|
||||
$('#tts_voicemap_block').empty()
|
||||
voiceMapEntries = []
|
||||
|
||||
// Get characters in current chat
|
||||
const characters = getCharacters()
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
saveSettingsDebounced,
|
||||
setGenerationParamsFromPreset
|
||||
} from "../script.js";
|
||||
import { getCfgPrompt } from "./extensions/cfg/util.js";
|
||||
import { getCfgPrompt } from "./cfg-scale.js";
|
||||
import { MAX_CONTEXT_DEFAULT } from "./power-user.js";
|
||||
import { getTextTokens, tokenizers } from "./tokenizers.js";
|
||||
import {
|
||||
|
|
|
@ -181,7 +181,7 @@ async function bindUserNameToPersona() {
|
|||
|
||||
export function selectCurrentPersona() {
|
||||
const personaName = power_user.personas[user_avatar];
|
||||
if (personaName && name1 !== personaName) {
|
||||
if (personaName) {
|
||||
const lockedPersona = chat_metadata['persona'];
|
||||
if (lockedPersona && lockedPersona !== user_avatar && power_user.persona_show_notifications) {
|
||||
toastr.info(
|
||||
|
@ -191,7 +191,10 @@ export function selectCurrentPersona() {
|
|||
);
|
||||
}
|
||||
|
||||
setUserName(personaName);
|
||||
if (personaName !== name1) {
|
||||
console.log(`Auto-updating user name to ${personaName}`);
|
||||
setUserName(personaName);
|
||||
}
|
||||
|
||||
const descriptor = power_user.persona_descriptions[user_avatar];
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ export {
|
|||
fixMarkdown,
|
||||
power_user,
|
||||
send_on_enter_options,
|
||||
getContextSettings,
|
||||
};
|
||||
|
||||
export const MAX_CONTEXT_DEFAULT = 4096;
|
||||
|
@ -131,7 +132,6 @@ let power_user = {
|
|||
|
||||
custom_css: '',
|
||||
|
||||
|
||||
waifuMode: false,
|
||||
movingUI: false,
|
||||
movingUIState: {},
|
||||
|
@ -139,7 +139,7 @@ let power_user = {
|
|||
noShadows: false,
|
||||
theme: 'Default (Dark) 1.7.1',
|
||||
|
||||
|
||||
gestures: true,
|
||||
auto_swipe: false,
|
||||
auto_swipe_minimum_length: 0,
|
||||
auto_swipe_blacklist: [],
|
||||
|
@ -249,6 +249,20 @@ const storage_keys = {
|
|||
expand_message_actions: 'ExpandMessageActions',
|
||||
};
|
||||
|
||||
const contextControls = [
|
||||
// Power user context scoped settings
|
||||
{ id: "context_story_string", property: "story_string", isCheckbox: false, isGlobalSetting: false },
|
||||
{ id: "context_example_separator", property: "example_separator", isCheckbox: false, isGlobalSetting: false },
|
||||
{ id: "context_chat_start", property: "chat_start", isCheckbox: false, isGlobalSetting: false },
|
||||
|
||||
// Existing power user settings
|
||||
{ id: "always-force-name2-checkbox", property: "always_force_name2", isCheckbox: true, isGlobalSetting: true },
|
||||
{ id: "trim_sentences_checkbox", property: "trim_sentences", isCheckbox: true, isGlobalSetting: true },
|
||||
{ id: "include_newline_checkbox", property: "include_newline", isCheckbox: true, isGlobalSetting: true },
|
||||
{ id: "custom_stopping_strings", property: "custom_stopping_strings", isCheckbox: false, isGlobalSetting: true },
|
||||
{ id: "custom_stopping_strings_macro", property: "custom_stopping_strings_macro", isCheckbox: true, isGlobalSetting: true }
|
||||
];
|
||||
|
||||
let browser_has_focus = true;
|
||||
const debug_functions = [];
|
||||
|
||||
|
@ -399,6 +413,7 @@ function switchMessageActions() {
|
|||
power_user.expand_message_actions = value === null ? false : value == "true";
|
||||
$("body").toggleClass("expandMessageActions", power_user.expand_message_actions);
|
||||
$("#expandMessageActions").prop("checked", power_user.expand_message_actions);
|
||||
$('.extraMesButtons, .extraMesButtonsHint').removeAttr('style');
|
||||
}
|
||||
|
||||
function switchUiMode() {
|
||||
|
@ -836,6 +851,18 @@ switchMesIDDisplay();
|
|||
switchTokenCount();
|
||||
switchMessageActions();
|
||||
|
||||
function getExampleMessagesBehavior() {
|
||||
if (power_user.strip_examples) {
|
||||
return 'strip';
|
||||
}
|
||||
|
||||
if (power_user.pin_examples) {
|
||||
return 'keep';
|
||||
}
|
||||
|
||||
return 'normal';
|
||||
}
|
||||
|
||||
function loadPowerUserSettings(settings, data) {
|
||||
// Load from settings.json
|
||||
if (settings.power_user !== undefined) {
|
||||
|
@ -864,7 +891,6 @@ function loadPowerUserSettings(settings, data) {
|
|||
const timestamps = localStorage.getItem(storage_keys.timestamps_enabled);
|
||||
const mesIDDisplay = localStorage.getItem(storage_keys.mesIDDisplay_enabled);
|
||||
const expandMessageActions = localStorage.getItem(storage_keys.expand_message_actions);
|
||||
console.log(expandMessageActions)
|
||||
power_user.fast_ui_mode = fastUi === null ? true : fastUi == "true";
|
||||
power_user.movingUI = movingUI === null ? false : movingUI == "true";
|
||||
power_user.noShadows = noShadows === null ? false : noShadows == "true";
|
||||
|
@ -873,7 +899,6 @@ function loadPowerUserSettings(settings, data) {
|
|||
power_user.timestamps_enabled = timestamps === null ? true : timestamps == "true";
|
||||
power_user.mesIDDisplay_enabled = mesIDDisplay === null ? true : mesIDDisplay == "true";
|
||||
power_user.expand_message_actions = expandMessageActions === null ? true : expandMessageActions == "true";
|
||||
console.log(power_user.expand_message_actions)
|
||||
power_user.avatar_style = Number(localStorage.getItem(storage_keys.avatar_style) ?? avatar_styles.ROUND);
|
||||
//power_user.chat_display = Number(localStorage.getItem(storage_keys.chat_display) ?? chat_styles.DEFAULT);
|
||||
power_user.chat_width = Number(localStorage.getItem(storage_keys.chat_width) ?? 50);
|
||||
|
@ -902,6 +927,7 @@ function loadPowerUserSettings(settings, data) {
|
|||
$('#continue_on_send').prop("checked", power_user.continue_on_send);
|
||||
$('#quick_continue').prop("checked", power_user.quick_continue);
|
||||
$('#mes_continue').css('display', power_user.quick_continue ? '' : 'none');
|
||||
$('#gestures-checkbox').prop("checked", power_user.gestures);
|
||||
$('#auto_swipe').prop("checked", power_user.auto_swipe);
|
||||
$('#auto_swipe_minimum_length').val(power_user.auto_swipe_minimum_length);
|
||||
$('#auto_swipe_blacklist').val(power_user.auto_swipe_blacklist.join(", "));
|
||||
|
@ -911,6 +937,8 @@ function loadPowerUserSettings(settings, data) {
|
|||
$('#fuzzy_search_checkbox').prop("checked", power_user.fuzzy_search);
|
||||
$('#persona_show_notifications').prop("checked", power_user.persona_show_notifications);
|
||||
$('#encode_tags').prop("checked", power_user.encode_tags);
|
||||
$('#example_messages_behavior').val(getExampleMessagesBehavior());
|
||||
$(`#example_messages_behavior option[value="${getExampleMessagesBehavior()}"]`).prop("selected", true);
|
||||
|
||||
$("#console_log_prompts").prop("checked", power_user.console_log_prompts);
|
||||
$('#auto_fix_generated_markdown').prop("checked", power_user.auto_fix_generated_markdown);
|
||||
|
@ -921,8 +949,6 @@ function loadPowerUserSettings(settings, data) {
|
|||
$("#confirm_message_delete").prop("checked", power_user.confirm_message_delete !== undefined ? !!power_user.confirm_message_delete : true);
|
||||
$("#spoiler_free_mode").prop("checked", power_user.spoiler_free_mode);
|
||||
$("#collapse-newlines-checkbox").prop("checked", power_user.collapse_newlines);
|
||||
$("#pin-examples-checkbox").prop("checked", power_user.pin_examples);
|
||||
$("#remove-examples-checkbox").prop("checked", power_user.strip_examples);
|
||||
$("#always-force-name2-checkbox").prop("checked", power_user.always_force_name2);
|
||||
$("#trim_sentences_checkbox").prop("checked", power_user.trim_sentences);
|
||||
$("#include_newline_checkbox").prop("checked", power_user.include_newline);
|
||||
|
@ -1070,14 +1096,28 @@ function switchMaxContextSize() {
|
|||
}
|
||||
}
|
||||
|
||||
function loadContextSettings() {
|
||||
const controls = [
|
||||
{ id: "context_story_string", property: "story_string", isCheckbox: false },
|
||||
{ id: "context_example_separator", property: "example_separator", isCheckbox: false },
|
||||
{ id: "context_chat_start", property: "chat_start", isCheckbox: false },
|
||||
];
|
||||
// Fetch a compiled object of all preset settings
|
||||
function getContextSettings() {
|
||||
let compiledSettings = {};
|
||||
|
||||
controls.forEach(control => {
|
||||
contextControls.forEach((control) => {
|
||||
let value = control.isGlobalSetting ? power_user[control.property] : power_user.context[control.property];
|
||||
|
||||
// Force to a boolean if the setting is a checkbox
|
||||
if (control.isCheckbox) {
|
||||
value = !!value;
|
||||
}
|
||||
|
||||
compiledSettings[control.property] = value;
|
||||
});
|
||||
|
||||
return compiledSettings;
|
||||
}
|
||||
|
||||
// TODO: Maybe add a refresh button to reset settings to preset
|
||||
// TODO: Add "global state" if a preset doesn't set the power_user checkboxes
|
||||
function loadContextSettings() {
|
||||
contextControls.forEach(control => {
|
||||
const $element = $(`#${control.id}`);
|
||||
|
||||
if (control.isCheckbox) {
|
||||
|
@ -1086,8 +1126,16 @@ function loadContextSettings() {
|
|||
$element.val(power_user.context[control.property]);
|
||||
}
|
||||
|
||||
// If the setting already exists, no need to duplicate it
|
||||
// TODO: Maybe check the power_user object for the setting instead of a flag?
|
||||
$element.on('input', function () {
|
||||
power_user.context[control.property] = control.isCheckbox ? !!$(this).prop('checked') : $(this).val();
|
||||
const value = control.isCheckbox ? !!$(this).prop('checked') : $(this).val();
|
||||
if (control.isGlobalSetting) {
|
||||
power_user[control.property] = value;
|
||||
} else {
|
||||
power_user.context[control.property] = value;
|
||||
}
|
||||
|
||||
saveSettingsDebounced();
|
||||
if (!control.isCheckbox) {
|
||||
resetScrollHeight($element);
|
||||
|
@ -1113,15 +1161,24 @@ function loadContextSettings() {
|
|||
}
|
||||
|
||||
power_user.context.preset = name;
|
||||
controls.forEach(control => {
|
||||
contextControls.forEach(control => {
|
||||
if (preset[control.property] !== undefined) {
|
||||
power_user.context[control.property] = preset[control.property];
|
||||
if (control.isGlobalSetting) {
|
||||
power_user[control.property] = preset[control.property];
|
||||
} else {
|
||||
power_user.context[control.property] = preset[control.property];
|
||||
}
|
||||
|
||||
const $element = $(`#${control.id}`);
|
||||
|
||||
if (control.isCheckbox) {
|
||||
$element.prop('checked', power_user.context[control.property]).trigger('input');
|
||||
$element
|
||||
.prop('checked', control.isGlobalSetting ? power_user[control.property] : power_user.context[control.property])
|
||||
.trigger('input');
|
||||
} else {
|
||||
$element.val(power_user.context[control.property]).trigger('input');
|
||||
$element
|
||||
.val(control.isGlobalSetting ? power_user[control.property] : power_user.context[control.property])
|
||||
.trigger('input');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1164,7 +1221,7 @@ function highlightDefaultContext() {
|
|||
export function fuzzySearchCharacters(searchValue) {
|
||||
const fuse = new Fuse(characters, {
|
||||
keys: [
|
||||
{ name: 'data.name', weight: 5 },
|
||||
{ name: 'data.name', weight: 8 },
|
||||
{ name: 'data.description', weight: 3 },
|
||||
{ name: 'data.mes_example', weight: 3 },
|
||||
{ name: 'data.scenario', weight: 2 },
|
||||
|
@ -1849,28 +1906,6 @@ $(document).ready(() => {
|
|||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#pin-examples-checkbox").change(function () {
|
||||
if ($(this).prop("checked")) {
|
||||
$("#remove-examples-checkbox").prop("checked", false).prop("disabled", true);
|
||||
power_user.strip_examples = false;
|
||||
} else {
|
||||
$("#remove-examples-checkbox").prop("disabled", false);
|
||||
}
|
||||
power_user.pin_examples = !!$(this).prop("checked");
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$("#remove-examples-checkbox").change(function () {
|
||||
if ($(this).prop("checked")) {
|
||||
$("#pin-examples-checkbox").prop("checked", false).prop("disabled", true);
|
||||
power_user.pin_examples = false;
|
||||
} else {
|
||||
$("#pin-examples-checkbox").prop("disabled", false);
|
||||
}
|
||||
power_user.strip_examples = !!$(this).prop("checked");
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
// include newline is the child of trim sentences
|
||||
// if include newline is checked, trim sentences must be checked
|
||||
// if trim sentences is unchecked, include newline must be unchecked
|
||||
|
@ -1929,6 +1964,31 @@ $(document).ready(() => {
|
|||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#example_messages_behavior').on('change', function () {
|
||||
const selectedOption = String($(this).find(':selected').val());
|
||||
console.log('Setting example messages behavior to', selectedOption);
|
||||
|
||||
switch (selectedOption) {
|
||||
case 'normal':
|
||||
power_user.pin_examples = false;
|
||||
power_user.strip_examples = false;
|
||||
break;
|
||||
case 'keep':
|
||||
power_user.pin_examples = true;
|
||||
power_user.strip_examples = false;
|
||||
break;
|
||||
case 'strip':
|
||||
power_user.pin_examples = false;
|
||||
power_user.strip_examples = true;
|
||||
break;
|
||||
}
|
||||
|
||||
console.debug('power_user.pin_examples', power_user.pin_examples);
|
||||
console.debug('power_user.strip_examples', power_user.strip_examples);
|
||||
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
// Settings that go to local storage
|
||||
$("#fast_ui_mode").change(function () {
|
||||
power_user.fast_ui_mode = $(this).prop("checked");
|
||||
|
@ -2122,6 +2182,11 @@ $(document).ready(() => {
|
|||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#gestures-checkbox').on('change', function () {
|
||||
power_user.gestures = !!$('#gestures-checkbox').prop('checked');
|
||||
saveSettingsDebounced();
|
||||
});
|
||||
|
||||
$('#auto_swipe').on('input', function () {
|
||||
power_user.auto_swipe = !!$(this).prop('checked');
|
||||
saveSettingsDebounced();
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
import { groups, selected_group } from "./group-chats.js";
|
||||
import { instruct_presets } from "./instruct-mode.js";
|
||||
import { kai_settings } from "./kai-settings.js";
|
||||
import { context_presets, power_user } from "./power-user.js";
|
||||
import { context_presets, getContextSettings, power_user } from "./power-user.js";
|
||||
import {
|
||||
textgenerationwebui_preset_names,
|
||||
textgenerationwebui_presets,
|
||||
|
@ -104,6 +104,7 @@ class PresetManager {
|
|||
|
||||
async updatePreset() {
|
||||
const selected = $(this.select).find("option:selected");
|
||||
console.log(selected)
|
||||
|
||||
if (selected.val() == 'gui') {
|
||||
toastr.info('Cannot update GUI preset');
|
||||
|
@ -236,7 +237,7 @@ class PresetManager {
|
|||
case "textgenerationwebui":
|
||||
return textgenerationwebui_settings;
|
||||
case "context":
|
||||
const context_preset = structuredClone(power_user.context);
|
||||
const context_preset = getContextSettings();
|
||||
context_preset['name'] = name || power_user.context.preset;
|
||||
return context_preset;
|
||||
case "instruct":
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = 'settingsSearch';
|
||||
async function addSettingsSearchHTML() {
|
||||
|
||||
const html = `
|
||||
<div class="wide100p">
|
||||
<div class="justifyLeft">
|
||||
<textarea id="settingsSearch" class="wide100p textarea_compact" rows="1" placeholder="Search Settings"></textarea>
|
||||
</div>
|
||||
</div>`
|
||||
|
||||
$("#user-settings-block").prepend(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for settings that match the search string and highlight them.
|
||||
*/
|
||||
async function searchSettings() {
|
||||
removeHighlighting(); // Remove previous highlights
|
||||
let searchString = $("#settingsSearch").val();
|
||||
let searchableText = $("#user-settings-block-content"); // Get the HTML block
|
||||
const searchString = String($("#settingsSearch").val());
|
||||
const searchableText = $("#user-settings-block-content"); // Get the HTML block
|
||||
if (searchString.trim() !== "") {
|
||||
highlightMatchingElements(searchableText[0], searchString); // Highlight matching elements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the element is a child of a header element
|
||||
* @param {HTMLElement | Text | Document | Comment} element Settings block HTML element
|
||||
* @returns {boolean} True if the element is a child of a header element, false otherwise
|
||||
*/
|
||||
function isParentHeader(element) {
|
||||
return $(element).closest('h4, h3').length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively highlight elements that match the search string
|
||||
* @param {HTMLElement | Text | Document | Comment} element Settings block HTML element
|
||||
* @param {string} searchString Search string
|
||||
*/
|
||||
function highlightMatchingElements(element, searchString) {
|
||||
$(element).contents().each(function () {
|
||||
const isTextNode = this.nodeType === Node.TEXT_NODE;
|
||||
|
@ -41,17 +41,14 @@ function highlightMatchingElements(element, searchString) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove highlighting from previously highlighted elements.
|
||||
*/
|
||||
function removeHighlighting() {
|
||||
$(".highlighted").removeClass("highlighted"); // Remove CSS class from previously highlighted elements
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
//addSettingsSearchHTML();
|
||||
$('#settingsSearch').on('input change', searchSettings);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -194,10 +194,10 @@ async function getStats() {
|
|||
|
||||
/**
|
||||
* Asynchronously recreates the stats file from chat files.
|
||||
*
|
||||
*
|
||||
* Sends a POST request to the "/recreatestats" endpoint. If the request fails,
|
||||
* it displays an error notification and throws an error.
|
||||
*
|
||||
*
|
||||
* @throws {Error} If the request to recreate stats is unsuccessful.
|
||||
*/
|
||||
async function recreateStats() {
|
||||
|
@ -330,23 +330,12 @@ async function statMesProcess(line, type, characters, this_chid, oldMesssage) {
|
|||
updateStats();
|
||||
}
|
||||
|
||||
jQuery(() => {
|
||||
function init() {
|
||||
$(".rm_stats_button").on('click', function () {
|
||||
characterStatsHandler(characters, this_chid);
|
||||
});
|
||||
// Wait for debug functions to load, then add the refresh stats function
|
||||
registerDebugFunction('refreshStats', 'Refresh Stat File', 'Recreates the stats file based on existing chat files', recreateStats);
|
||||
}
|
||||
|
||||
// Check every 100ms if registerDebugFunction is defined (this is bad lmao)
|
||||
const interval = setInterval(() => {
|
||||
if (typeof registerDebugFunction !== 'undefined') {
|
||||
clearInterval(interval); // Clear the interval once the function is found
|
||||
init(); // Initialize your code
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
|
||||
export function initStats() {
|
||||
$(".rm_stats_button").on('click', function () {
|
||||
characterStatsHandler(characters, this_chid);
|
||||
});
|
||||
// Wait for debug functions to load, then add the refresh stats function
|
||||
registerDebugFunction('refreshStats', 'Refresh Stat File', 'Recreates the stats file based on existing chat files', recreateStats);
|
||||
}
|
||||
|
||||
export { userStatsHandler, characterStatsHandler, getStats, statMesProcess, charStats };
|
||||
|
|
|
@ -3,6 +3,7 @@ System-wide Replacement Macros:
|
|||
<li><tt>{{user}}</tt> - your current Persona username</li>
|
||||
<li><tt>{{char}}</tt> - the Character's name</li>
|
||||
<li><tt>{{input}}</tt> - the user input</li>
|
||||
<li><tt>{{// (note)}}</tt> - you can leave a note here, and the macro will be replaced with blank content. Not visible for the AI.</li>
|
||||
<li><tt>{{time}}</tt> - the current time</li>
|
||||
<li><tt>{{date}}</tt> - the current date</li>
|
||||
<li><tt>{{weekday}}</tt> - the current weekday</li>
|
||||
|
|
|
@ -182,6 +182,7 @@ export function getTokenizerModel() {
|
|||
return oai_settings.openai_model;
|
||||
}
|
||||
|
||||
const turbo0301Tokenizer = 'gpt-3.5-turbo-0301';
|
||||
const turboTokenizer = 'gpt-3.5-turbo';
|
||||
const gpt4Tokenizer = 'gpt-4';
|
||||
const gpt2Tokenizer = 'gpt2';
|
||||
|
@ -197,6 +198,9 @@ export function getTokenizerModel() {
|
|||
if (oai_settings.windowai_model.includes('gpt-4')) {
|
||||
return gpt4Tokenizer;
|
||||
}
|
||||
else if (oai_settings.windowai_model.includes('gpt-3.5-turbo-0301')) {
|
||||
return turbo0301Tokenizer;
|
||||
}
|
||||
else if (oai_settings.windowai_model.includes('gpt-3.5-turbo')) {
|
||||
return turboTokenizer;
|
||||
}
|
||||
|
@ -213,6 +217,9 @@ export function getTokenizerModel() {
|
|||
if (oai_settings.openrouter_model.includes('gpt-4')) {
|
||||
return gpt4Tokenizer;
|
||||
}
|
||||
else if (oai_settings.openrouter_model.includes('gpt-3.5-turbo-0301')) {
|
||||
return turbo0301Tokenizer;
|
||||
}
|
||||
else if (oai_settings.openrouter_model.includes('gpt-3.5-turbo')) {
|
||||
return turboTokenizer;
|
||||
}
|
||||
|
|
|
@ -498,7 +498,7 @@ export function countOccurrences(string, character) {
|
|||
let count = 0;
|
||||
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
if (string[i] === character) {
|
||||
if (string.substring(i, i + character.length) === character) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
@ -870,7 +870,7 @@ export async function saveBase64AsFile(base64Data, characterName, filename = "",
|
|||
// If the response is successful, get the saved image path from the server's response
|
||||
if (response.ok) {
|
||||
const responseData = await response.json();
|
||||
return responseData.path;
|
||||
return responseData.path.replace(/\\/g, '/'); // Replace backslashes with forward slashes
|
||||
} else {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || 'Failed to upload the image to the server');
|
||||
|
|
|
@ -279,6 +279,11 @@ table.responsiveTable {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.img_enlarged_container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.img_enlarged_container pre code,
|
||||
.mes_text pre code {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
@ -1171,6 +1176,14 @@ input[type="file"] {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.bulk_select_checkbox {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
#rm_print_characters_block.bulk_select .wide100pLess70px {
|
||||
width: calc(100% - 85px);
|
||||
}
|
||||
|
||||
#rm_print_characters_block {
|
||||
overflow-y: auto;
|
||||
flex-grow: 1;
|
||||
|
@ -1506,13 +1519,13 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
|||
z-index: 3001;
|
||||
}
|
||||
|
||||
#bg_menu_content {
|
||||
.bg_list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: calc(var(--sheldWidth) - 10px);
|
||||
max-width: 100vw;
|
||||
max-width: 100svw;
|
||||
justify-content: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.bg_example {
|
||||
|
@ -1531,6 +1544,26 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
.bg_example.locked {
|
||||
outline: 2px solid var(--golden);
|
||||
}
|
||||
|
||||
.bg_example:hover.locked .bg_example_lock {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg_example:hover:not(.locked) .bg_example_unlock {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg_example:hover[custom="true"] .bg_example_edit {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg_example:hover[custom="false"] .bg_example_copy {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.BGSampleTitle {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
@ -1545,13 +1578,17 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
|||
font-size: calc(var(--fontScale) * 0.9em);
|
||||
}
|
||||
|
||||
.bg_example[custom="true"] .BGSampleTitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg_button {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
opacity: 0.8;
|
||||
border-radius: 50%;
|
||||
font-size: 20px;
|
||||
color: var(--black70a);
|
||||
|
@ -1579,6 +1616,16 @@ input[type=search]:focus::-webkit-search-cancel-button {
|
|||
left: 10px;
|
||||
}
|
||||
|
||||
.bg_example_copy {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.bg_example_lock,
|
||||
.bg_example_unlock {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.add_bg_but {
|
||||
cursor: pointer;
|
||||
opacity: 0.1;
|
||||
|
@ -3016,8 +3063,9 @@ a {
|
|||
.img_enlarged {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
padding: 10px 0;
|
||||
border-radius: 2px;
|
||||
border: 1px solid transparent;
|
||||
outline: 1px solid var(--SmartThemeBorderColor);
|
||||
}
|
||||
|
||||
.cropper-container {
|
||||
|
@ -3356,19 +3404,6 @@ a {
|
|||
flex-direction: column;
|
||||
}
|
||||
|
||||
.openai_preset_buttons,
|
||||
.preset_buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.openai_preset_buttons select,
|
||||
.preset_buttons select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#ReverseProxyWarningMessage {
|
||||
display: none;
|
||||
}
|
||||
|
|
119
server.js
119
server.js
|
@ -726,7 +726,7 @@ app.post("/getchat", jsonParser, function (request, response) {
|
|||
const lines = data.split('\n');
|
||||
|
||||
// Iterate through the array of strings and parse each line as JSON
|
||||
const jsonData = lines.map(tryParse).filter(x => x);
|
||||
const jsonData = lines.map((l) => { try { return JSON.parse(l); } catch (_) { } }).filter(x => x);
|
||||
return response.send(jsonData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@ -1535,8 +1535,8 @@ app.post("/delchat", jsonParser, function (request, response) {
|
|||
app.post('/renamebackground', jsonParser, function (request, response) {
|
||||
if (!request.body) return response.sendStatus(400);
|
||||
|
||||
const oldFileName = path.join('public/backgrounds/', sanitize(request.body.old_bg));
|
||||
const newFileName = path.join('public/backgrounds/', sanitize(request.body.new_bg));
|
||||
const oldFileName = path.join(DIRECTORIES.backgrounds, sanitize(request.body.old_bg));
|
||||
const newFileName = path.join(DIRECTORIES.backgrounds, sanitize(request.body.new_bg));
|
||||
|
||||
if (!fs.existsSync(oldFileName)) {
|
||||
console.log('BG file not found');
|
||||
|
@ -1841,37 +1841,27 @@ function getImages(path) {
|
|||
.sort(Intl.Collator().compare);
|
||||
}
|
||||
|
||||
app.post("/getallchatsofcharacter", jsonParser, function (request, response) {
|
||||
app.post("/getallchatsofcharacter", jsonParser, async function (request, response) {
|
||||
if (!request.body) return response.sendStatus(400);
|
||||
|
||||
var char_dir = (request.body.avatar_url).replace('.png', '')
|
||||
fs.readdir(chatsPath + char_dir, (err, files) => {
|
||||
if (err) {
|
||||
console.log('found error in history loading');
|
||||
console.error(err);
|
||||
const characterDirectory = (request.body.avatar_url).replace('.png', '');
|
||||
|
||||
try {
|
||||
const chatsDirectory = path.join(chatsPath, characterDirectory);
|
||||
const files = fs.readdirSync(chatsDirectory);
|
||||
const jsonFiles = files.filter(file => path.extname(file) === '.jsonl');
|
||||
|
||||
if (jsonFiles.length === 0) {
|
||||
response.send({ error: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// filter for JSON files
|
||||
const jsonFiles = files.filter(file => path.extname(file) === '.jsonl');
|
||||
|
||||
// sort the files by name
|
||||
//jsonFiles.sort().reverse();
|
||||
// print the sorted file names
|
||||
var chatData = {};
|
||||
let ii = jsonFiles.length; //this is the number of files belonging to the character
|
||||
if (ii !== 0) {
|
||||
//console.log('found '+ii+' chat logs to load');
|
||||
for (let i = jsonFiles.length - 1; i >= 0; i--) {
|
||||
const file = jsonFiles[i];
|
||||
const fileStream = fs.createReadStream(chatsPath + char_dir + '/' + file);
|
||||
|
||||
const fullPathAndFile = chatsPath + char_dir + '/' + file
|
||||
const stats = fs.statSync(fullPathAndFile);
|
||||
const fileSizeInKB = (stats.size / 1024).toFixed(2) + "kb";
|
||||
|
||||
//console.log(fileSizeInKB);
|
||||
const jsonFilesPromise = jsonFiles.map((file) => {
|
||||
return new Promise(async (res) => {
|
||||
const pathToFile = path.join(chatsPath, characterDirectory, file);
|
||||
const fileStream = fs.createReadStream(pathToFile);
|
||||
const stats = fs.statSync(pathToFile);
|
||||
const fileSizeInKB = `${(stats.size / 1024).toFixed(2)}kb`;
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: fileStream,
|
||||
|
@ -1885,34 +1875,37 @@ app.post("/getallchatsofcharacter", jsonParser, function (request, response) {
|
|||
lastLine = line;
|
||||
});
|
||||
rl.on('close', () => {
|
||||
ii--;
|
||||
if (lastLine) {
|
||||
rl.close();
|
||||
|
||||
let jsonData = tryParse(lastLine);
|
||||
if (jsonData && (jsonData.name !== undefined || jsonData.character_name !== undefined)) {
|
||||
chatData[i] = {};
|
||||
chatData[i]['file_name'] = file;
|
||||
chatData[i]['file_size'] = fileSizeInKB;
|
||||
chatData[i]['chat_items'] = itemCounter - 1;
|
||||
chatData[i]['mes'] = jsonData['mes'] || '[The chat is empty]';
|
||||
chatData[i]['last_mes'] = jsonData['send_date'] || Date.now();
|
||||
if (lastLine) {
|
||||
const jsonData = tryParse(lastLine);
|
||||
if (jsonData && (jsonData.name || jsonData.character_name)) {
|
||||
const chatData = {};
|
||||
|
||||
chatData['file_name'] = file;
|
||||
chatData['file_size'] = fileSizeInKB;
|
||||
chatData['chat_items'] = itemCounter - 1;
|
||||
chatData['mes'] = jsonData['mes'] || '[The chat is empty]';
|
||||
chatData['last_mes'] = jsonData['send_date'] || Date.now();
|
||||
|
||||
res(chatData);
|
||||
} else {
|
||||
console.log('Found an invalid or corrupted chat file: ' + fullPathAndFile);
|
||||
console.log('Found an invalid or corrupted chat file:', pathToFile);
|
||||
res({});
|
||||
}
|
||||
}
|
||||
if (ii === 0) {
|
||||
//console.log('ii count went to zero, responding with chatData');
|
||||
response.send(chatData);
|
||||
}
|
||||
//console.log('successfully closing getallchatsofcharacter');
|
||||
rl.close();
|
||||
});
|
||||
};
|
||||
} else {
|
||||
//console.log('Found No Chats. Exiting Load Routine.');
|
||||
response.send({ error: true });
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
const chatData = await Promise.all(jsonFilesPromise);
|
||||
const validFiles = chatData.filter(i => i.file_name);
|
||||
|
||||
return response.send(validFiles);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return response.send({ error: true });
|
||||
}
|
||||
});
|
||||
|
||||
function getPngName(file) {
|
||||
|
@ -2812,7 +2805,7 @@ app.post("/openai_bias", jsonParser, async function (request, response) {
|
|||
}
|
||||
|
||||
try {
|
||||
const tokens = tokenizer.encode(entry.text);
|
||||
const tokens = getEntryTokens(entry.text);
|
||||
|
||||
for (const token of tokens) {
|
||||
result[token] = entry.value;
|
||||
|
@ -2825,6 +2818,28 @@ app.post("/openai_bias", jsonParser, async function (request, response) {
|
|||
// not needed for cached tokenizers
|
||||
//tokenizer.free();
|
||||
return response.send(result);
|
||||
|
||||
/**
|
||||
* Gets tokenids for a given entry
|
||||
* @param {string} text Entry text
|
||||
* @returns {Uint32Array} Array of token ids
|
||||
*/
|
||||
function getEntryTokens(text) {
|
||||
// Get raw token ids from JSON array
|
||||
if (text.trim().startsWith('[') && text.trim().endsWith(']')) {
|
||||
try {
|
||||
const json = JSON.parse(text);
|
||||
if (Array.isArray(json) && json.every(x => typeof x === 'number')) {
|
||||
return new Uint32Array(json);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, get token ids from tokenizer
|
||||
return tokenizer.encode(text);
|
||||
}
|
||||
});
|
||||
|
||||
function convertChatMLPrompt(messages) {
|
||||
|
|
|
@ -79,7 +79,7 @@ function registerEndpoints(app, jsonParser) {
|
|||
return response.status(409).send(`Directory already exists at ${extensionPath}`);
|
||||
}
|
||||
|
||||
await git.clone(url, extensionPath);
|
||||
await git.clone(url, extensionPath, { '--depth': 1 });
|
||||
console.log(`Extension has been cloned at ${extensionPath}`);
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,43 @@
|
|||
const fetch = require('node-fetch').default;
|
||||
const { getBasicAuthHeader, delay } = require('./util');
|
||||
|
||||
/**
|
||||
* Sanitizes a string.
|
||||
* @param {string} x String to sanitize
|
||||
* @returns {string} Sanitized string
|
||||
*/
|
||||
function safeStr(x) {
|
||||
x = String(x);
|
||||
for (let i = 0; i < 16; i++) {
|
||||
x = x.replace(/ /g, ' ');
|
||||
}
|
||||
x = x.trim();
|
||||
x = x.replace(/^[\s,.]+|[\s,.]+$/g, '');
|
||||
return x;
|
||||
}
|
||||
|
||||
const splitStrings = [
|
||||
', extremely',
|
||||
', intricate,',
|
||||
];
|
||||
|
||||
const dangerousPatterns = '[]【】()()|::';
|
||||
|
||||
/**
|
||||
* Removes patterns from a string.
|
||||
* @param {string} x String to sanitize
|
||||
* @param {string} pattern Pattern to remove
|
||||
* @returns {string} Sanitized string
|
||||
*/
|
||||
function removePattern(x, pattern) {
|
||||
for (let i = 0; i < pattern.length; i++) {
|
||||
let p = pattern[i];
|
||||
let regex = new RegExp("\\" + p, 'g');
|
||||
x = x.replace(regex, '');
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the endpoints for the Stable Diffusion API extension.
|
||||
* @param {import("express").Express} app Express app
|
||||
|
@ -233,7 +270,8 @@ function registerEndpoints(app, jsonParser) {
|
|||
});
|
||||
|
||||
if (!result.ok) {
|
||||
throw new Error('SD WebUI returned an error.');
|
||||
const text = await result.text();
|
||||
throw new Error('SD WebUI returned an error.', { cause: text });
|
||||
}
|
||||
|
||||
const data = await result.json();
|
||||
|
@ -275,6 +313,40 @@ function registerEndpoints(app, jsonParser) {
|
|||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* SD prompt expansion using GPT-2 text generation model.
|
||||
* Adapted from: https://github.com/lllyasviel/Fooocus/blob/main/modules/expansion.py
|
||||
*/
|
||||
app.post('/api/sd/expand', jsonParser, async (request, response) => {
|
||||
const originalPrompt = request.body.prompt;
|
||||
|
||||
if (!originalPrompt) {
|
||||
console.warn('No prompt provided for SD expansion.');
|
||||
return response.send({ prompt: '' });
|
||||
}
|
||||
|
||||
console.log('Refine prompt input:', originalPrompt);
|
||||
const splitString = splitStrings[Math.floor(Math.random() * splitStrings.length)];
|
||||
let prompt = safeStr(originalPrompt) + splitString;
|
||||
|
||||
try {
|
||||
const task = 'text-generation';
|
||||
const module = await import('./transformers.mjs');
|
||||
const pipe = await module.default.getPipeline(task);
|
||||
|
||||
const result = await pipe(prompt, { num_beams: 1, max_new_tokens: 256, do_sample: true });
|
||||
|
||||
const newText = result[0].generated_text;
|
||||
const newPrompt = safeStr(removePattern(newText, dangerousPatterns));
|
||||
console.log('Refine prompt output:', newPrompt);
|
||||
|
||||
return response.send({ prompt: newPrompt });
|
||||
} catch {
|
||||
console.warn('Failed to load transformers.js pipeline.');
|
||||
return response.send({ prompt: originalPrompt });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -95,6 +95,10 @@ function getTokenizerModel(requestModel) {
|
|||
return 'gpt-4';
|
||||
}
|
||||
|
||||
if (requestModel.includes('gpt-3.5-turbo-0301')) {
|
||||
return 'gpt-3.5-turbo-0301';
|
||||
}
|
||||
|
||||
if (requestModel.includes('gpt-3.5-turbo')) {
|
||||
return 'gpt-3.5-turbo';
|
||||
}
|
||||
|
@ -296,8 +300,8 @@ function registerEndpoints(app, jsonParser) {
|
|||
return res.send({ "token_count": num_tokens });
|
||||
}
|
||||
|
||||
const tokensPerName = model.includes('gpt-4') ? 1 : -1;
|
||||
const tokensPerMessage = model.includes('gpt-4') ? 3 : 4;
|
||||
const tokensPerName = queryModel.includes('gpt-3.5-turbo-0301') ? -1 : 1;
|
||||
const tokensPerMessage = queryModel.includes('gpt-3.5-turbo-0301') ? 4 : 3;
|
||||
const tokensPadding = 3;
|
||||
|
||||
const tokenizer = getTiktokenTokenizer(model);
|
||||
|
@ -319,7 +323,7 @@ function registerEndpoints(app, jsonParser) {
|
|||
|
||||
// NB: Since 2023-10-14, the GPT-3.5 Turbo 0301 model shoves in 7-9 extra tokens to every message.
|
||||
// More details: https://community.openai.com/t/gpt-3-5-turbo-0301-showing-different-behavior-suddenly/431326/14
|
||||
if (queryModel.endsWith('-0301')) {
|
||||
if (queryModel.includes('gpt-3.5-turbo-0301')) {
|
||||
num_tokens += 9;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { pipeline, env, RawImage } from 'sillytavern-transformers';
|
||||
import { pipeline, env, RawImage, Pipeline } from 'sillytavern-transformers';
|
||||
import { getConfigValue } from './util.js';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
|
@ -28,8 +28,18 @@ const tasks = {
|
|||
pipeline: null,
|
||||
configField: 'extras.embeddingModel',
|
||||
},
|
||||
'text-generation': {
|
||||
defaultModel: 'Cohee/fooocus_expansion-onnx',
|
||||
pipeline: null,
|
||||
configField: 'extras.promptExpansionModel',
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a RawImage object from a base64-encoded image.
|
||||
* @param {string} image Base64-encoded image
|
||||
* @returns {Promise<RawImage|null>} Object representing the image
|
||||
*/
|
||||
async function getRawImage(image) {
|
||||
try {
|
||||
const buffer = Buffer.from(image, 'base64');
|
||||
|
@ -43,6 +53,11 @@ async function getRawImage(image) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the model to use for a given transformers.js task.
|
||||
* @param {string} task The task to get the model for
|
||||
* @returns {string} The model to use for the given task
|
||||
*/
|
||||
function getModelForTask(task) {
|
||||
const defaultModel = tasks[task].defaultModel;
|
||||
|
||||
|
@ -55,6 +70,11 @@ function getModelForTask(task) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transformers.js pipeline for a given task.
|
||||
* @param {string} task The task to get the pipeline for
|
||||
* @returns {Promise<Pipeline>} Pipeline for the task
|
||||
*/
|
||||
async function getPipeline(task) {
|
||||
if (tasks[task].pipeline) {
|
||||
return tasks[task].pipeline;
|
||||
|
|
Loading…
Reference in New Issue