Merge pull request #4096 from SillyTavern/TopbarFadeMUIAndGetWorldEntryRefactors UI performance optimizations and refactoring

f33db29e76f474e4f938720d2ca06ba556af1d12

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
27 files changed, +2246 -2127Ignore whitespace
public/css/animations.css+10 -0
@@ -55,6 +55,7 @@
5555
56/* Flashing for highlighting animation */56/* Flashing for highlighting animation */
57@keyframes flash {57@keyframes flash {
58
58 0%,59 0%,
59 50%,60 50%,
60 100% {61 100% {
@@ -122,3 +123,12 @@
122 border-top-color: var(--progFlashColor);123 border-top-color: var(--progFlashColor);
123 }124 }
124}125}
126
127/* Scroll delay animations */
128@keyframes hide-scroll {
129
130 from,
131 to {
132 overflow-y: hidden;
133 }
134}
public/css/character-group-overlay.css+1 -1
@@ -1,5 +1,5 @@
1#rm_print_characters_block.group_overlay_mode_select .character_select {1#rm_print_characters_block.group_overlay_mode_select .character_select {
2 transition: background-color 0.4s ease;2 transition: background-color var(--animation-duration-3x) ease;
3 background-color: rgba(170, 170, 170, 0.15);3 background-color: rgba(170, 170, 170, 0.15);
4}4}
55
public/css/loader.css+1 -1
@@ -18,7 +18,7 @@
1818
19#load-spinner {19#load-spinner {
20 --spinner-size: 2em;20 --spinner-size: 2em;
21 transition: all 300ms ease-out;21 transition: all var(--animation-duration-2x) ease-out;
22 opacity: 1;22 opacity: 1;
23 top: calc(50% - var(--spinner-size) / 2);23 top: calc(50% - var(--spinner-size) / 2);
24 left: calc(50% - var(--spinner-size) / 2);24 left: calc(50% - var(--spinner-size) / 2);
public/css/login.css+1 -1
@@ -23,7 +23,7 @@ body.login .userSelect {
23 width: 30%;23 width: 30%;
24 cursor: pointer;24 cursor: pointer;
25 margin: 5px 0;25 margin: 5px 0;
26 transition: background-color 0.15s ease-in-out;26 transition: background-color var(--animation-duration) ease-in-out;
27 display: flex;27 display: flex;
28 align-items: center;28 align-items: center;
29 justify-content: center;29 justify-content: center;
public/css/logprobs.css+1 -1
@@ -43,7 +43,7 @@
43 height: 25px;43 height: 25px;
44 margin-left: 5px;44 margin-left: 5px;
45 opacity: 0.5;45 opacity: 0.5;
46 transition: all 250ms;46 transition: all var(--animation-duration-2x);
47 position: unset !important;47 position: unset !important;
48}48}
4949
public/css/mobile-styles.css+9 -0
@@ -237,6 +237,15 @@
237 top: var(--topBarBlockSize) !important;237 top: var(--topBarBlockSize) !important;
238 left: 0 !important;238 left: 0 !important;
239 backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));239 backdrop-filter: blur(calc(var(--SmartThemeBlurStrength) * 2));
240
241 @starting-style {
242 height: 0;
243 }
244 }
245
246 #left-nav-panel:not(.openDrawer),
247 #right-nav-panel:not(.openDrawer) {
248 height: 0;
240 }249 }
241250
242 /*251 /*
public/css/promptmanager.css+2 -2
@@ -79,7 +79,7 @@
79 justify-content: center;79 justify-content: center;
80 margin-left: 0.25em;80 margin-left: 0.25em;
81 cursor: pointer;81 cursor: pointer;
82 transition: 0.3s ease-in-out;82 transition: var(--animation-duration-2x) ease-in-out;
83 height: 20px;83 height: 20px;
84 width: 20px;84 width: 20px;
85 filter: drop-shadow(0px 0px 2px black);85 filter: drop-shadow(0px 0px 2px black);
@@ -239,7 +239,7 @@
239 flex-direction: column;239 flex-direction: column;
240 justify-content: center;240 justify-content: center;
241 margin-left: 0.25em;241 margin-left: 0.25em;
242 transition: 0.3s ease-in-out;242 transition: var(--animation-duration-2x) ease-in-out;
243 filter: drop-shadow(0px 0px 2px black);243 filter: drop-shadow(0px 0px 2px black);
244}244}
245245
public/css/rm-groups.css+1 -1
@@ -172,7 +172,7 @@
172#rm_group_members .group_member:not(.disabled) .right_menu_button[data-action="disable"] {172#rm_group_members .group_member:not(.disabled) .right_menu_button[data-action="disable"] {
173 opacity: 0.4;173 opacity: 0.4;
174 filter: brightness(0.5);174 filter: brightness(0.5);
175 transition: all 0.2s ease-in-out;175 transition: all var(--animation-duration-2x) ease-in-out;
176}176}
177177
178/* #rm_group_members .right_menu_button[data-action="speak"]:hover, */178/* #rm_group_members .right_menu_button[data-action="speak"]:hover, */
public/css/select2-overrides.css+3 -3
@@ -125,7 +125,7 @@
125 background-color: unset;125 background-color: unset;
126 color: var(--SmartThemeBodyColor);126 color: var(--SmartThemeBodyColor);
127 opacity: 0.5;127 opacity: 0.5;
128 transition: opacity 200ms ease-in-out;128 transition: opacity var(--animation-duration-2x) ease-in-out;
129 position: relative;129 position: relative;
130}130}
131131
@@ -203,7 +203,7 @@
203/* Styling for choice remove icon */203/* Styling for choice remove icon */
204span.select2.select2-container .select2-selection__choice__remove {204span.select2.select2-container .select2-selection__choice__remove {
205 cursor: pointer;205 cursor: pointer;
206 transition: background-color 0.3s;206 transition: background-color var(--animation-duration-2x);
207 color: var(--SmartThemeBodyColor);207 color: var(--SmartThemeBodyColor);
208 background-color: var(--black50a);208 background-color: var(--black50a);
209}209}
@@ -220,7 +220,7 @@ span.select2.select2-container .select2-selection__choice__remove:hover {
220220
221.select2_choice_clickable_buttonstyle+span.select2-container .select2-selection__choice__display {221.select2_choice_clickable_buttonstyle+span.select2-container .select2-selection__choice__display {
222 cursor: pointer;222 cursor: pointer;
223 transition: background-color 0.3s;223 transition: background-color var(--animation-duration-2x);
224 color: var(--SmartThemeBodyColor);224 color: var(--SmartThemeBodyColor);
225 background-color: var(--black50a);225 background-color: var(--black50a);
226 white-space: break-spaces;226 white-space: break-spaces;
public/css/st-tailwind.css+5 -1
@@ -332,6 +332,10 @@
332 overflow: hidden;332 overflow: hidden;
333}333}
334334
335.overflowYScroll {
336 overflow-y: scroll;
337}
338
335.padding0 {339.padding0 {
336 padding: 0;340 padding: 0;
337}341}
@@ -480,7 +484,7 @@
480}484}
481485
482.hoverglow {486.hoverglow {
483 transition: opacity 200ms;487 transition: opacity var(--animation-duration-2x);
484}488}
485489
486.hoverglow:hover {490.hoverglow:hover {
public/css/tags.css+1 -1
@@ -167,7 +167,7 @@
167}167}
168168
169.rm_tag_filter .tag.actionable {169.rm_tag_filter .tag.actionable {
170 transition: opacity 200ms;170 transition: opacity var(--animation-duration-2x);
171}171}
172172
173.rm_tag_filter .tag:hover {173.rm_tag_filter .tag:hover {
public/css/toggle-dependent.css+7 -0
@@ -19,6 +19,7 @@ body:not(.tts) #ttsExtensionNarrateAll {
19}19}
2020
21body.no-hotswap .hotswap,21body.no-hotswap .hotswap,
22body.no-hotswap .hotswap~hr,
22body.no-timer .mes_timer,23body.no-timer .mes_timer,
23body.no-timestamps .timestamp,24body.no-timestamps .timestamp,
24body.no-tokenCount .tokenCounterDisplay,25body.no-tokenCount .tokenCounterDisplay,
@@ -28,6 +29,12 @@ body.hideChatAvatars .mesAvatarWrapper .avatar {
28 display: none !important;29 display: none !important;
29}30}
3031
32body.no-hotswap #CharListButtonAndHotSwaps .flex-container {
33 flex-wrap: nowrap;
34 align-items: flex-end;
35 flex-flow: row;
36}
37
31body.hideChatAvatars .last_mes:not(.smallSysMes) {38body.hideChatAvatars .last_mes:not(.smallSysMes) {
32 padding-bottom: 20px !important;39 padding-bottom: 20px !important;
33}40}
public/css/world-info.css+6 -2
@@ -198,7 +198,11 @@
198}198}
199199
200.world_entry {200.world_entry {
201 transition: opacity 500ms;201 transition: opacity var(--animation-duration-3x);
202}
203
204.world_entry_edit {
205 display: contents;
202}206}
203207
204.disabledWIEntry {208.disabledWIEntry {
@@ -267,7 +271,7 @@ select.keyselect+span.select2-container .select2-selection--multiple {
267271
268 opacity: 0.5;272 opacity: 0.5;
269 color: var(--SmartThemeBodyColor);273 color: var(--SmartThemeBodyColor);
270 transition: opacity 0.3s;274 transition: opacity var(--animation-duration-2x);
271}275}
272276
273.switch_input_type_icon:hover {277.switch_input_type_icon:hover {
public/index.html+614 -606
@@ -140,7 +140,7 @@
140 <div data-preset-manager-import="novel" class="margin0 menu_button_icon menu_button" title="Import preset" data-i18n="[title]Import preset">140 <div data-preset-manager-import="novel" class="margin0 menu_button_icon menu_button" title="Import preset" data-i18n="[title]Import preset">
141 <i class="fa-fw fa-solid fa-file-import"></i>141 <i class="fa-fw fa-solid fa-file-import"></i>
142 </div>142 </div>
143 <div data-preset-manager-export="novel" class="margin0 menu_button_icon menu_button" title="Export preset" data-i18n="[title]Export preset">143 <div data-preset-manager-export="novel" class="margin0 menu_button_icon menu_button" title="Export preset" data-i18n="[title]Export preset">
144 <i class="fa-fw fa-solid fa-file-export"></i>144 <i class="fa-fw fa-solid fa-file-export"></i>
145 </div>145 </div>
146 <div data-preset-manager-delete="novel" class="margin0 menu_button_icon menu_button" title="Delete the preset" data-i18n="[title]Delete the preset">146 <div data-preset-manager-delete="novel" class="margin0 menu_button_icon menu_button" title="Delete the preset" data-i18n="[title]Delete the preset">
@@ -663,7 +663,7 @@
663 <div data-source="openrouter" class="range-block">663 <div data-source="openrouter" class="range-block">
664 <div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">664 <div class="range-block-title" title="Allow compressing requests by removing messages from the middle of the prompt.">
665 <span data-i18n="Middle-out Transform">Middle-out Transform</span>665 <span data-i18n="Middle-out Transform">Middle-out Transform</span>
666 <a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>666 <a href="https://openrouter.ai/docs/transforms" target="_blank" rel="noreferrer noopener" class="note-link-span fa-solid fa-circle-question"></a>
667 </div>667 </div>
668 <div class="wide100p">668 <div class="wide100p">
669 <select id="openrouter_middleout" class="text_pole">669 <select id="openrouter_middleout" class="text_pole">
@@ -3301,16 +3301,16 @@
3301 <span id="vertexai_service_account_info"></span>3301 <span id="vertexai_service_account_info"></span>
3302 </div>3302 </div>
3303 <textarea id="vertexai_service_account_json" class="text_pole textarea_compact" rows="4" placeholder='Paste your Service Account JSON content here, e.g.:3303 <textarea id="vertexai_service_account_json" class="text_pole textarea_compact" rows="4" placeholder='Paste your Service Account JSON content here, e.g.:
3304{3304 {
3305 "type": "service_account",3305 "type": "service_account",
3306 "project_id": "your-project-id",3306 "project_id": "your-project-id",
3307 "private_key_id": "...",3307 "private_key_id": "...",
3308 "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",3308 "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
3309 "client_email": "...",3309 "client_email": "...",
3310 "client_id": "...",3310 "client_id": "...",
3311 "auth_uri": "https://accounts.google.com/o/oauth2/auth",3311 "auth_uri": "https://accounts.google.com/o/oauth2/auth",
3312 "token_uri": "https://oauth2.googleapis.com/token"3312 "token_uri": "https://oauth2.googleapis.com/token"
3313}'></textarea>3313 }'></textarea>
3314 <div data-for="vertexai_service_account_json" class="neutral_warning" data-i18n="For privacy reasons, your Service Account JSON content will be hidden after you reload the page.">3314 <div data-for="vertexai_service_account_json" class="neutral_warning" data-i18n="For privacy reasons, your Service Account JSON content will be hidden after you reload the page.">
3315 For privacy reasons, your Service Account JSON content will be hidden after you reload the page.3315 For privacy reasons, your Service Account JSON content will be hidden after you reload the page.
3316 </div>3316 </div>
@@ -3827,7 +3827,7 @@
3827 <input id="instruct_bind_to_context" type="checkbox" style="display:none;" />3827 <input id="instruct_bind_to_context" type="checkbox" style="display:none;" />
3828 <small><i class="fa-solid fa-link menu_button margin0"></i></small>3828 <small><i class="fa-solid fa-link menu_button margin0"></i></small>
3829 </label>3829 </label>
3830 <label id="instruct_enabled_label"for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">3830 <label id="instruct_enabled_label" for="instruct_enabled" class="checkbox_label flex1" title="Enable Instruct Mode" data-i18n="[title]instruct_enabled">
3831 <input id="instruct_enabled" type="checkbox" style="display:none;" />3831 <input id="instruct_enabled" type="checkbox" style="display:none;" />
3832 <small><i class="fa-solid fa-power-off menu_button togglable margin0"></i></small>3832 <small><i class="fa-solid fa-power-off menu_button togglable margin0"></i></small>
3833 </label>3833 </label>
@@ -5286,7 +5286,7 @@
5286 <div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">5286 <div class="flex-container wide100p alignitemscenter spaceBetween flexNoGap">
5287 <div class="flex-container alignItemsBaseline wide100p">5287 <div class="flex-container alignItemsBaseline wide100p">
5288 <div class="flex1 flex-container alignItemsBaseline">5288 <div class="flex1 flex-container alignItemsBaseline">
5289 <h3 class="margin0" >5289 <h3 class="margin0">
5290 <span data-i18n="Persona Management">Persona Management</span>5290 <span data-i18n="Persona Management">Persona Management</span>
5291 <a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">5291 <a href="https://docs.sillytavern.app/usage/core-concepts/personas/" target="_blank">
5292 <span class="fa-solid fa-circle-question note-link-span"></span>5292 <span class="fa-solid fa-circle-question note-link-span"></span>
@@ -5457,10 +5457,12 @@
5457 <div class="right_menu_button fa-solid fa-list-ul" id="rm_button_characters" title="Select/Create Characters" data-i18n="[title]Select/Create Characters"></div>5457 <div class="right_menu_button fa-solid fa-list-ul" id="rm_button_characters" title="Select/Create Characters" data-i18n="[title]Select/Create Characters"></div>
5458 </div>5458 </div>
5459 <div id="HotSwapWrapper" class="alignitemscenter flex-container margin0auto wide100p">5459 <div id="HotSwapWrapper" class="alignitemscenter flex-container margin0auto wide100p">
5460 <div class="hotswap avatars_inline scroll-reset-container expander" data-i18n="[no_favs]Favorite characters to add them to HotSwaps" no_favs="Favorite characters to add them to HotSwaps"></div>5460 <div class="hotswap avatars_inline scroll-reset-container expander" data-i18n="[no_favs]Favorite characters to add them to HotSwaps" no_favs="Favorite characters to add them to HotSwaps">
5461 </div>
5462 <hr class="wide100p">
5461 </div>5463 </div>
5462 </div>5464 </div>
5463 <hr>5465
5464 <!-- this div structure must be preserved until group peeking can adjust -->5466 <!-- this div structure must be preserved until group peeking can adjust -->
5465 <div id="rm_PinAndTabs">5467 <div id="rm_PinAndTabs">
5466 <div id="right-nav-panel-tabs" class="">5468 <div id="right-nav-panel-tabs" class="">
@@ -5485,340 +5487,343 @@
5485 </div>5487 </div>
5486 </div>5488 </div>
5487 <!-- end group peeking cope structure-->5489 <!-- end group peeking cope structure-->
5488 <div name="Solo Char Create/Edit Panel" id="rm_ch_create_block" class="right_menu flex-container flexFlowColumn" style="display: none;">5490
5489 <form id="form_create" action="javascript:void(null);" method="post" enctype="multipart/form-data">5491 <div class="scrollableInner">
5490 <div id="avatar-and-name-block">5492 <div name="Solo Char Create/Edit Panel" id="rm_ch_create_block" class="right_menu flex-container flexFlowColumn" style="display: none;">
5491 <div id="name_div">5493 <form id="form_create" action="javascript:void(null);" method="post" enctype="multipart/form-data">
5492 <input id="character_name_pole" name="ch_name" class="text_pole" data-i18n="[placeholder]Name this character" placeholder="Name this character" value="" autocomplete="off">5494 <div id="avatar-and-name-block">
5493 <div class="extension_token_counter">5495 <div id="name_div">
5494 <span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="character_name_pole" data-token-permanent="true">counting...</span>5496 <input id="character_name_pole" name="ch_name" class="text_pole" data-i18n="[placeholder]Name this character" placeholder="Name this character" value="" autocomplete="off">
5495 </div>5497 <div class="extension_token_counter">
5496 </div>5498 <span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="character_name_pole" data-token-permanent="true">counting...</span>
5497 <div class="flex-container flexFlowColumn expander flexNoGap">5499 </div>
5498 <div id="avatar_div" class="avatar_div buttons_block alignitemsflexstart justifySpaceBetween flexnowrap">5500 </div>
5499 <label id="avatar_div_div" class="add_avatar avatar" for="add_avatar_button" title="Click to select a new avatar for this character" data-i18n="[title]Click to select a new avatar for this character">5501 <div class="flex-container flexFlowColumn expander flexNoGap">
5500 <img id="avatar_load_preview" src="img/ai4.png" alt="avatar">5502 <div id="avatar_div" class="avatar_div buttons_block alignitemsflexstart justifySpaceBetween flexnowrap">
5501 <input hidden type="file" id="add_avatar_button" name="avatar" accept="image/*">5503 <label id="avatar_div_div" class="add_avatar avatar" for="add_avatar_button" title="Click to select a new avatar for this character" data-i18n="[title]Click to select a new avatar for this character">
5502 </label>5504 <img id="avatar_load_preview" src="img/ai4.png" alt="avatar">
5503 <div class="flex-container" id="avatar_controls">5505 <input hidden type="file" id="add_avatar_button" name="avatar" accept="image/*">
5504 <div class="form_create_bottom_buttons_block buttons_block">5506 </label>
5505 <div id="rm_button_back" class="menu_button fa-solid fa-left-long "></div>5507 <div class="flex-container" id="avatar_controls">
5506 <!-- <div id="renameCharButton" class="menu_button fa-solid fa-user-pen" title="Rename Character"></div> -->5508 <div class="form_create_bottom_buttons_block buttons_block">
5507 <div id="favorite_button" class="menu_button fa-solid fa-star" title="Add to Favorites" data-i18n="[title]Add to Favorites"></div>5509 <div id="rm_button_back" class="menu_button fa-solid fa-left-long "></div>
5508 <input type="hidden" id="fav_checkbox" name="fav" />5510 <!-- <div id="renameCharButton" class="menu_button fa-solid fa-user-pen" title="Rename Character"></div> -->
5509 <div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>5511 <div id="favorite_button" class="menu_button fa-solid fa-star" title="Add to Favorites" data-i18n="[title]Add to Favorites"></div>
5510 <div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore&#10;&#10;Click to load&#10;Shift-click to open 'Link to World Info' popup" data-i18n="[title]world_button_title"></div>5512 <input type="hidden" id="fav_checkbox" name="fav" />
5511 <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div>5513 <div id="advanced_div" class="menu_button fa-solid fa-book " title="Advanced Definitions" data-i18n="[title]Advanced Definition"></div>
5512 <div id="char_connections_button" class="menu_button fa-solid fa-face-smile" title="Connected Personas" data-i18n="[title]Connected Personas"></div>5514 <div id="world_button" class="menu_button fa-solid fa-globe" title="Character Lore&#10;&#10;Click to load&#10;Shift-click to open 'Link to World Info' popup" data-i18n="[title]world_button_title"></div>
5513 <div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>5515 <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div>
5514 <!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->5516 <div id="char_connections_button" class="menu_button fa-solid fa-face-smile" title="Connected Personas" data-i18n="[title]Connected Personas"></div>
5515 <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->5517 <div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
5516 <div id="dupe_button" class="menu_button fa-solid fa-clone " title="Duplicate Character" data-i18n="[title]Duplicate Character"></div>5518 <!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
5517 <label for="create_button" id="create_button_label" class="menu_button fa-solid fa-user-check" title="Create Character" data-i18n="[title]Create Character">5519 <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
5518 <input type="submit" id="create_button" name="create_button">5520 <div id="dupe_button" class="menu_button fa-solid fa-clone " title="Duplicate Character" data-i18n="[title]Duplicate Character"></div>
5519 </label>5521 <label for="create_button" id="create_button_label" class="menu_button fa-solid fa-user-check" title="Create Character" data-i18n="[title]Create Character">
5520 <div id="delete_button" class="menu_button fa-solid fa-skull red_button" title="Delete Character" data-i18n="[title]Delete Character"></div>5522 <input type="submit" id="create_button" name="create_button">
5521 </div>5523 </label>
5522 <label class="flex1 height100p" for="char-management-dropdown">5524 <div id="delete_button" class="menu_button fa-solid fa-skull red_button" title="Delete Character" data-i18n="[title]Delete Character"></div>
5523 <select id="char-management-dropdown" class="text_pole">5525 </div>
5524 <option value="default" disabled selected data-i18n="More...">More...</option>5526 <label class="flex1 height100p" for="char-management-dropdown">
5525 <option id="set_character_world" data-i18n="Link to World Info">5527 <select id="char-management-dropdown" class="text_pole">
5526 Link to World Info5528 <option value="default" disabled selected data-i18n="More...">More...</option>
5527 </option>5529 <option id="set_character_world" data-i18n="Link to World Info">
5528 <option id="import_character_info" data-i18n="Import Card Lore">5530 Link to World Info
5529 Import Card Lore
5530 </option>
5531 <option id="set_chat_scenario" data-i18n="Scenario Override">
5532 Scenario Override
5533 </option>
5534 <option id="convert_to_persona" data-i18n="Convert to Persona">
5535 Convert to Persona
5536 </option>
5537 <option id="renameCharButton" data-i18n="Rename">
5538 Rename
5539 </option>
5540 <option id="character_source" data-i18n="Link to Source">
5541 Link to Source
5542 </option>
5543 <option id="replace_update" data-i18n="Replace / Update">
5544 Replace / Update
5545 </option>
5546 <option id="import_tags" data-i18n="Import Tags">
5547 Import Tags
5548 </option>
5549 <option id="set_as_assistant" data-i18n="Set / Unset as Welcome Page Assistant">
5550 Set / Unset as Welcome Page Assistant
5551 </option>
5552 <!--<option id="dupe_button">
5553 Duplicate
5554 </option>5531 </option>
5555 <option id="export_button">5532 <option id="import_character_info" data-i18n="Import Card Lore">
5556 Export5533 Import Card Lore
5557 </option>5534 </option>
5558 <option id="delete_button">5535 <option id="set_chat_scenario" data-i18n="Scenario Override">
5559 Delete5536 Scenario Override
5560 </option>-->5537 </option>
5561 </select>5538 <option id="convert_to_persona" data-i18n="Convert to Persona">
5562 </label>5539 Convert to Persona
5540 </option>
5541 <option id="renameCharButton" data-i18n="Rename">
5542 Rename
5543 </option>
5544 <option id="character_source" data-i18n="Link to Source">
5545 Link to Source
5546 </option>
5547 <option id="replace_update" data-i18n="Replace / Update">
5548 Replace / Update
5549 </option>
5550 <option id="import_tags" data-i18n="Import Tags">
5551 Import Tags
5552 </option>
5553 <option id="set_as_assistant" data-i18n="Set / Unset as Welcome Page Assistant">
5554 Set / Unset as Welcome Page Assistant
5555 </option>
5556 <!--<option id="dupe_button">
5557 Duplicate
5558 </option>
5559 <option id="export_button">
5560 Export
5561 </option>
5562 <option id="delete_button">
5563 Delete
5564 </option>-->
5565 </select>
5566 </label>
5567 </div>
5563 </div>5568 </div>
5564 </div>5569 <div id="tags_div">
5565 <div id="tags_div">5570 <div class="tag_controls">
5566 <div class="tag_controls">5571 <input id="tagInput" class="text_pole textarea_compact tag_input wide100p margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" />
5567 <input id="tagInput" class="text_pole textarea_compact tag_input wide100p margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" />5572 <div class="tags_view menu_button fa-solid fa-tags" title="View all tags" data-i18n="[title]View all tags"></div>
5568 <div class="tags_view menu_button fa-solid fa-tags" title="View all tags" data-i18n="[title]View all tags"></div>5573 </div>
5574 <div id="tagList" class="tags"></div>
5569 </div>5575 </div>
5570 <div id="tagList" class="tags"></div>
5571 </div>5576 </div>
5572 </div>5577 </div>
5573 </div>
5574 <hr>
5575 <div id="spoiler_free_desc" class="flex-container flexFlowColumn flex1 flexNoGap">
5576 <div id="creators_notes_div" class="title_restorable flexGap5">
5577 <span class="flex1" data-i18n="Creator's Notes">Creator's Notes</span>
5578 <small id="creators_note_desc_hidden" data-i18n="Character details are hidden.">Character details are hidden.</small>
5579 <div id="creators_note_styles_button" class="margin0 menu_button fa-solid fa-palette fa-fw" title="Allow / Forbid the use of global styles for this character." data-i18n="[title]Allow / Forbid the use of global styles for this character."></div>
5580 <div id="spoiler_free_desc_button" class="margin0 menu_button fa-solid fa-eye fa-fw" title="Show / Hide Description and First Message" data-i18n="[title]Show / Hide Description and First Message"></div>
5581 </div>
5582 <div id="creator_notes_spoiler" class="flex1"></div>
5583 <!-- A button to show / hide description_div and description_textarea and first_message_div and firstmessage_textarea-->
5584 </div>
5585 <div id="descriptionWrapper" class="flex-container flexFlowColumn flex1">
5586 <hr>5578 <hr>
5587 <div id="description_div" class="title_restorable">5579 <div id="spoiler_free_desc" class="flex-container flexFlowColumn flex1 flexNoGap">
5588 <div class="flex-container alignitemscenter">5580 <div id="creators_notes_div" class="title_restorable flexGap5">
5589 <span data-i18n="Character Description" class="mdhotkey_location">Description</span>5581 <span class="flex1" data-i18n="Creator's Notes">Creator's Notes</span>
5590 <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="description_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>5582 <small id="creators_note_desc_hidden" data-i18n="Character details are hidden.">Character details are hidden.</small>
5591 <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-description" class="notes-link" target="_blank">5583 <div id="creators_note_styles_button" class="margin0 menu_button fa-solid fa-palette fa-fw" title="Allow / Forbid the use of global styles for this character." data-i18n="[title]Allow / Forbid the use of global styles for this character."></div>
5592 <span class="fa-solid fa-circle-question note-link-span"></span>5584 <div id="spoiler_free_desc_button" class="margin0 menu_button fa-solid fa-eye fa-fw" title="Show / Hide Description and First Message" data-i18n="[title]Show / Hide Description and First Message"></div>
5593 </a>
5594 </div>5585 </div>
5595 <div id="character_open_media_overrides" class="menu_button menu_button_icon open_media_overrides" title="Click to allow/forbid the use of external media for this character." data-i18n="[title]Click to allow/forbid the use of external media for this character.">5586 <div id="creator_notes_spoiler" class="flex1"></div>
5596 <i id="character_media_allowed_icon" class="fa-solid fa-fw fa-link"></i>5587 <!-- A button to show / hide description_div and description_textarea and first_message_div and firstmessage_textarea-->
5597 <i id="character_media_forbidden_icon" class="fa-solid fa-fw fa-link-slash"></i>
5598 <span data-i18n="Ext. Media">
5599 Ext. Media
5600 </span>
5601 </div>
5602 </div>
5603 <textarea id="description_textarea" class="mdHotkeys" data-i18n="[placeholder]Describe your character's physical and mental traits here." placeholder="Describe your character's physical and mental traits here." name="description" placeholder=""></textarea>
5604 <div class="extension_token_counter">
5605 <span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="description_textarea" data-token-permanent="true">counting...</span>
5606 </div>5588 </div>
5607 </div>5589 <div id="descriptionWrapper" class="flex-container flexFlowColumn flex1">
5608 <div id="firstMessageWrapper" class="flex-container flexFlowColumn flex1">5590 <hr>
5609 <div id="first_message_div" class="title_restorable">5591 <div id="description_div" class="title_restorable">
5610 <div class="flex-container alignitemscenter flex1">5592 <div class="flex-container alignitemscenter">
5611 <span data-i18n="First message" class="mdhotkey_location">First message</span>5593 <span data-i18n="Character Description" class="mdhotkey_location">Description</span>
5612 <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="firstmessage_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>5594 <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="description_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
5613 <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#first-message" class="notes-link" target="_blank">5595 <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-description" class="notes-link" target="_blank">
5614 <span class="fa-solid fa-circle-question note-link-span"></span>5596 <span class="fa-solid fa-circle-question note-link-span"></span>
5615 </a>5597 </a>
5598 </div>
5599 <div id="character_open_media_overrides" class="menu_button menu_button_icon open_media_overrides" title="Click to allow/forbid the use of external media for this character." data-i18n="[title]Click to allow/forbid the use of external media for this character.">
5600 <i id="character_media_allowed_icon" class="fa-solid fa-fw fa-link"></i>
5601 <i id="character_media_forbidden_icon" class="fa-solid fa-fw fa-link-slash"></i>
5602 <span data-i18n="Ext. Media">
5603 Ext. Media
5604 </span>
5605 </div>
5616 </div>5606 </div>
5617 <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">5607 <textarea id="description_textarea" class="mdHotkeys" data-i18n="[placeholder]Describe your character's physical and mental traits here." placeholder="Describe your character's physical and mental traits here." name="description" placeholder=""></textarea>
5618 <span data-i18n="Alt. Greetings">5608 <div class="extension_token_counter">
5619 Alt. Greetings5609 <span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="description_textarea" data-token-permanent="true">counting...</span>
5620 </span>
5621 </div>5610 </div>
5622 </div>5611 </div>
5623 <textarea class="mdHotkeys" 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." name="first_mes" placeholder=""></textarea>5612 <div id="firstMessageWrapper" class="flex-container flexFlowColumn flex1">
5624 <div class="extension_token_counter">5613 <div id="first_message_div" class="title_restorable">
5625 <span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="firstmessage_textarea">counting...</span>5614 <div class="flex-container alignitemscenter flex1">
5626 </div>5615 <span data-i18n="First message" class="mdhotkey_location">First message</span>
5627 </div>5616 <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="firstmessage_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
5628 <!-- these divs are invisible and used for server communication purposes -->5617 <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#first-message" class="notes-link" target="_blank">
5629 <div id="hidden-divs">5618 <span class="fa-solid fa-circle-question note-link-span"></span>
5630 <input id="character_json_data" name="json_data" type="hidden">5619 </a>
5631 <input id="avatar_url_pole" name="avatar_url" type="hidden">5620 </div>
5632 <input id="selected_chat_pole" name="chat" type="hidden">5621 <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">
5633 <input id="create_date_pole" name="create_date" type="hidden">5622 <span data-i18n="Alt. Greetings">
5634 <input id="last_mes_pole" name="last_mes" type="hidden">5623 Alt. Greetings
5635 <input id="character_world" name="world" type="hidden">5624 </span>
5636 </div>
5637 <!-- now back to normal divs for display purposes-->
5638 </form>
5639 </div>
5640 <div name="Group Chat Edit Panel" id="rm_group_chats_block" class="right_menu flex-container flexNoGap">
5641 <div class="inline-drawer wide100p flexFlowColumn">
5642 <div id="groupControlsToggle" class="inline-drawer-toggle inline-drawer-header">
5643 <span>
5644 <span data-i18n="Group Controls">Group Controls</span>
5645 <a href="https://docs.sillytavern.app/usage/core-concepts/groupchats/" class="notes-link" target="_blank">
5646 <span class="fa-solid fa-circle-question note-link-span"></span>
5647 </a>
5648 </span>
5649 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5650 </div>
5651 <div class="inline-drawer-content">
5652 <div id="group-metadata-controls" class="marginTopBot5">
5653 <div class="flex-container wide100p">
5654 <input id="rm_group_chat_name" class="text_pole flex1" type="text" name="chat_name" data-i18n="[placeholder]Chat Name (Optional)" placeholder="Chat Name (Optional)" />
5655 <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
5656 </div>
5657 <div id="group_tags_div" class="wide100p">
5658 <div class="tag_controls">
5659 <input id="groupTagInput" class="text_pole textarea_compact tag_input flex1 margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" />
5660 <div class="tags_view menu_button fa-solid fa-tags margin0" title="View all tags" data-i18n="[title]View all tags"></div>
5661 </div>5625 </div>
5662 <div id="groupTagList" class="tags paddingTopBot5"></div>
5663 </div>5626 </div>
5664 <div id="rm_group_top_bar" class="flex-container alignitemscenter spaceBetween width100p fontsize80p">5627 <textarea class="mdHotkeys" 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." name="first_mes" placeholder=""></textarea>
5665 <div>5628 <div class="extension_token_counter">
5666 <label class="add_avatar avatar flex-container justifyCenter" for="group_avatar_button" title="Click to select a new avatar for this group" data-i18n="[title]Click to select a new avatar for this group">5629 <span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="firstmessage_textarea">counting...</span>
5667 <div id="group_avatar_preview">5630 </div>
5668 <div class="avatar">5631 </div>
5669 <img src="img/ai4.png" alt="avatar">5632 <!-- these divs are invisible and used for server communication purposes -->
5670 </div>5633 <div id="hidden-divs">
5671 </div>5634 <input id="character_json_data" name="json_data" type="hidden">
5672 <input hidden type="file" id="group_avatar_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif, image/bmp">5635 <input id="avatar_url_pole" name="avatar_url" type="hidden">
5673 </label>5636 <input id="selected_chat_pole" name="chat" type="hidden">
5637 <input id="create_date_pole" name="create_date" type="hidden">
5638 <input id="last_mes_pole" name="last_mes" type="hidden">
5639 <input id="character_world" name="world" type="hidden">
5640 </div>
5641 <!-- now back to normal divs for display purposes-->
5642 </form>
5643 </div>
5644 <div name="Group Chat Edit Panel" id="rm_group_chats_block" class="right_menu flex-container flexNoGap">
5645 <div class="inline-drawer wide100p flexFlowColumn">
5646 <div id="groupControlsToggle" class="inline-drawer-toggle inline-drawer-header">
5647 <span>
5648 <span data-i18n="Group Controls">Group Controls</span>
5649 <a href="https://docs.sillytavern.app/usage/core-concepts/groupchats/" class="notes-link" target="_blank">
5650 <span class="fa-solid fa-circle-question note-link-span"></span>
5651 </a>
5652 </span>
5653 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5654 </div>
5655 <div class="inline-drawer-content">
5656 <div id="group-metadata-controls" class="marginTopBot5">
5657 <div class="flex-container wide100p">
5658 <input id="rm_group_chat_name" class="text_pole flex1" type="text" name="chat_name" data-i18n="[placeholder]Chat Name (Optional)" placeholder="Chat Name (Optional)" />
5659 <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore" data-i18n="[title]Chat Lore"></div>
5674 </div>5660 </div>
5675 <div name="GroupStragegyAndOrder" id="rm_group_buttons" class="flex-container paddingLeftRight5 flex2">5661 <div id="group_tags_div" class="wide100p">
5676 <div class="flex1 flexGap5">5662 <div class="tag_controls">
5677 <label for="rm_group_activation_strategy" class="flexnowrap width100p whitespacenowrap">5663 <input id="groupTagInput" class="text_pole textarea_compact tag_input flex1 margin0" data-i18n="[placeholder]Search / Create Tags" placeholder="Search / Create tags" />
5678 <span data-i18n="Group reply strategy">Group reply strategy</span>5664 <div class="tags_view menu_button fa-solid fa-tags margin0" title="View all tags" data-i18n="[title]View all tags"></div>
5679 </label>
5680 <select id="rm_group_activation_strategy">
5681 <option value="2" data-i18n="Manual">Manual</option>
5682 <option value="0" data-i18n="Natural order">Natural order</option>
5683 <option value="1" data-i18n="List order">List order</option>
5684 <option value="3" data-i18n="Pooled order">Pooled order</option>
5685 </select>
5686 </div>
5687 <div class="flex1 flexGap5">
5688 <label for="rm_group_generation_mode" class="flexnowrap width100p whitespacenowrap">
5689 <span data-i18n="Group generation handling mode">Group generation handling mode</span>
5690 </label>
5691 <select id="rm_group_generation_mode">
5692 <option value="0" data-i18n="Swap character cards">Swap character cards</option>
5693 <option value="1" data-i18n="Join character cards (exclude muted)">Join character cards (exclude muted)</option>
5694 <option value="2" data-i18n="Join character cards (include muted)">Join character cards (include muted)</option>
5695 </select>
5696 </div>
5697 <div class="flex1 flexGap5" title="Inserted before each part of the joined fields." data-i18n="[title]Inserted before each part of the joined fields.">
5698 <label for="rm_group_generation_mode_join_prefix" class="flexnowrap width100p whitespacenowrap">
5699 <span data-i18n="Join Prefix">Join Prefix</span>
5700 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)" title="When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)">
5701 </div>
5702 </label>
5703 <textarea id="rm_group_generation_mode_join_prefix" class="text_pole wide100p textarea_compact autoSetHeight" placeholder="&mdash;" rows="1"></textarea>
5704 </div>5665 </div>
5705 <div class="flex1 flexGap5" title="Inserted after each part of the joined fields." data-i18n="[title]Inserted after each part of the joined fields.">5666 <div id="groupTagList" class="tags paddingTopBot5"></div>
5706 <label for="rm_group_generation_mode_join_suffix" class="flexnowrap width100p whitespacenowrap">5667 </div>
5707 <span data-i18n="Join Suffix">Join Suffix</span>5668 <div id="rm_group_top_bar" class="flex-container alignitemscenter spaceBetween width100p fontsize80p">
5708 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)" title="When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)">5669 <div>
5670 <label class="add_avatar avatar flex-container justifyCenter" for="group_avatar_button" title="Click to select a new avatar for this group" data-i18n="[title]Click to select a new avatar for this group">
5671 <div id="group_avatar_preview">
5672 <div class="avatar">
5673 <img src="img/ai4.png" alt="avatar">
5674 </div>
5709 </div>5675 </div>
5676 <input hidden type="file" id="group_avatar_button" name="avatar" accept="image/png, image/jpeg, image/jpg, image/gif, image/bmp">
5710 </label>5677 </label>
5711 <textarea id="rm_group_generation_mode_join_suffix" class="text_pole wide100p textarea_compact autoSetHeight" placeholder="&mdash;" rows="1"></textarea>
5712 </div>5678 </div>
5713 </div>5679 <div name="GroupStragegyAndOrder" id="rm_group_buttons" class="flex-container paddingLeftRight5 flex2">
5714 <div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">5680 <div class="flex1 flexGap5">
5715 <div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-left-long"></div>5681 <label for="rm_group_activation_strategy" class="flexnowrap width100p whitespacenowrap">
5716 <div id="rm_group_scenario" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-scroll" title="Set a group chat scenario" data-i18n="[title]Set a group chat scenario"></div>5682 <span data-i18n="Group reply strategy">Group reply strategy</span>
5717 <div id="group_favorite_button" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-star" title="Add to Favorites" data-i18n="[title]Add to Favorites"></div>5683 </label>
5718 <input id="rm_group_fav" type="hidden" />5684 <select id="rm_group_activation_strategy">
5719 <div id="group_open_media_overrides" class="heightFitContent margin0 menu_button menu_button_icon open_media_overrides" title="Click to allow/forbid the use of external media for this group." data-i18n="[title]Click to allow/forbid the use of external media for this group.">5685 <option value="2" data-i18n="Manual">Manual</option>
5720 <i id="group_media_allowed_icon" class="fa-solid fa-fw fa-link"></i>5686 <option value="0" data-i18n="Natural order">Natural order</option>
5721 <i id="group_media_forbidden_icon" class="fa-solid fa-fw fa-link-slash"></i>5687 <option value="1" data-i18n="List order">List order</option>
5688 <option value="3" data-i18n="Pooled order">Pooled order</option>
5689 </select>
5690 </div>
5691 <div class="flex1 flexGap5">
5692 <label for="rm_group_generation_mode" class="flexnowrap width100p whitespacenowrap">
5693 <span data-i18n="Group generation handling mode">Group generation handling mode</span>
5694 </label>
5695 <select id="rm_group_generation_mode">
5696 <option value="0" data-i18n="Swap character cards">Swap character cards</option>
5697 <option value="1" data-i18n="Join character cards (exclude muted)">Join character cards (exclude muted)</option>
5698 <option value="2" data-i18n="Join character cards (include muted)">Join character cards (include muted)</option>
5699 </select>
5700 </div>
5701 <div class="flex1 flexGap5" title="Inserted before each part of the joined fields." data-i18n="[title]Inserted before each part of the joined fields.">
5702 <label for="rm_group_generation_mode_join_prefix" class="flexnowrap width100p whitespacenowrap">
5703 <span data-i18n="Join Prefix">Join Prefix</span>
5704 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)" title="When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)">
5705 </div>
5706 </label>
5707 <textarea id="rm_group_generation_mode_join_prefix" class="text_pole wide100p textarea_compact autoSetHeight" placeholder="&mdash;" rows="1"></textarea>
5708 </div>
5709 <div class="flex1 flexGap5" title="Inserted after each part of the joined fields." data-i18n="[title]Inserted after each part of the joined fields.">
5710 <label for="rm_group_generation_mode_join_suffix" class="flexnowrap width100p whitespacenowrap">
5711 <span data-i18n="Join Suffix">Join Suffix</span>
5712 <div class="fa-solid fa-circle-info opacity50p" data-i18n="[title]When 'Join character cards' is selected, all respective fields of the characters are being joined together.This means that in the story string for example all character descriptions will be joined to one big text.If you want those fields to be separated, you can define a prefix or suffix here.This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)" title="When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)">
5713 </div>
5714 </label>
5715 <textarea id="rm_group_generation_mode_join_suffix" class="text_pole wide100p textarea_compact autoSetHeight" placeholder="&mdash;" rows="1"></textarea>
5716 </div>
5722 </div>5717 </div>
5723 <div id="rm_group_submit" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-check" title="Create" data-i18n="[title]Create"></div>5718 <div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">
5724 <div id="rm_group_restore_avatar" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-images" title="Restore collage avatar" data-i18n="[title]Restore collage avatar"></div>5719 <div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-left-long"></div>
5725 <div id="rm_group_delete" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-trash-can" title="Delete" data-i18n="[title]Delete"></div>5720 <div id="rm_group_scenario" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-scroll" title="Set a group chat scenario" data-i18n="[title]Set a group chat scenario"></div>
5726 <div class="flex1">5721 <div id="group_favorite_button" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-star" title="Add to Favorites" data-i18n="[title]Add to Favorites"></div>
5727 <label class="checkbox_label whitespacenowrap">5722 <input id="rm_group_fav" type="hidden" />
5728 <input id="rm_group_allow_self_responses" type="checkbox" />5723 <div id="group_open_media_overrides" class="heightFitContent margin0 menu_button menu_button_icon open_media_overrides" title="Click to allow/forbid the use of external media for this group." data-i18n="[title]Click to allow/forbid the use of external media for this group.">
5729 <span data-i18n="Allow self responses">Allow self responses</span>5724 <i id="group_media_allowed_icon" class="fa-solid fa-fw fa-link"></i>
5730 </label>5725 <i id="group_media_forbidden_icon" class="fa-solid fa-fw fa-link-slash"></i>
5731 <label id="rm_group_automode_label" class="checkbox_label whitespacenowrap">5726 </div>
5732 <input id="rm_group_automode" type="checkbox" />5727 <div id="rm_group_submit" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-check" title="Create" data-i18n="[title]Create"></div>
5733 <span data-i18n="Auto Mode">Auto Mode</span>5728 <div id="rm_group_restore_avatar" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-images" title="Restore collage avatar" data-i18n="[title]Restore collage avatar"></div>
5734 <input id="rm_group_automode_delay" class="text_pole textarea_compact widthUnset" type="number" min="1" max="999" step="1" value="5" title="Auto Mode delay" data-i18n="[title]Auto Mode delay" />5729 <div id="rm_group_delete" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-trash-can" title="Delete" data-i18n="[title]Delete"></div>
5735 </label>5730 <div class="flex1">
5736 <label id="rm_group_hidemutedsprites_label" class="checkbox_label whitespacenowrap">5731 <label class="checkbox_label whitespacenowrap">
5737 <input id="rm_group_hidemutedsprites" type="checkbox" />5732 <input id="rm_group_allow_self_responses" type="checkbox" />
5738 <span data-i18n="Hide Muted Member Sprites">Hide Muted Member Sprites</span>5733 <span data-i18n="Allow self responses">Allow self responses</span>
5739 </label>5734 </label>
5735 <label id="rm_group_automode_label" class="checkbox_label whitespacenowrap">
5736 <input id="rm_group_automode" type="checkbox" />
5737 <span data-i18n="Auto Mode">Auto Mode</span>
5738 <input id="rm_group_automode_delay" class="text_pole textarea_compact widthUnset" type="number" min="1" max="999" step="1" value="5" title="Auto Mode delay" data-i18n="[title]Auto Mode delay" />
5739 </label>
5740 <label id="rm_group_hidemutedsprites_label" class="checkbox_label whitespacenowrap">
5741 <input id="rm_group_hidemutedsprites" type="checkbox" />
5742 <span data-i18n="Hide Muted Member Sprites">Hide Muted Member Sprites</span>
5743 </label>
5744 </div>
5740 </div>5745 </div>
5741 </div>5746 </div>
5742 </div>5747 </div>
5743 </div>5748 </div>
5744 </div>5749 </div>
5745 </div>5750 <div class="inline-drawer wide100p flexFlowColumn">
5746 <div class="inline-drawer wide100p flexFlowColumn">5751 <div id="groupCurrentMemberListToggle" class="inline-drawer-toggle inline-drawer-header">
5747 <div id="groupCurrentMemberListToggle" class="inline-drawer-toggle inline-drawer-header">5752 <span data-i18n="Current Members">Current Members </span><i id="groupCurrentMemberPopoutButton" class="fa-solid fa-window-restore menu_button"></i>
5748 <span data-i18n="Current Members">Current Members </span><i id="groupCurrentMemberPopoutButton" class="fa-solid fa-window-restore menu_button"></i>5753 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5749 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5750 </div>
5751 <div class="inline-drawer-content">
5752 <div id="currentGroupMembers" name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
5753 <div id="rm_group_members_pagination" class="rm_group_members_pagination group_pagination"></div>
5754 <div id="rm_group_members" class="rm_group_members overflowYAuto flex-container" group_empty_text="Group is empty." data-i18n="[group_empty_text]Group is empty."></div>
5755 </div>5754 </div>
5756 </div>5755 <div class="inline-drawer-content">
5757 </div>5756 <div id="currentGroupMembers" name="Current Group Members" class="flex-container flexFlowColumn overflowYAuto flex1">
5758 <div class="inline-drawer wide100p flexFlowColumn">5757 <div id="rm_group_members_pagination" class="rm_group_members_pagination group_pagination"></div>
5759 <div id="groupAddMemberListToggle" class="inline-drawer-toggle inline-drawer-header">5758 <div id="rm_group_members" class="rm_group_members overflowYAuto flex-container" group_empty_text="Group is empty." data-i18n="[group_empty_text]Group is empty."></div>
5760 <span data-i18n="Add Members">Add Members</span>
5761 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5762 </div>
5763 <div class="inline-drawer-content">
5764 <div name="Unadded Char List" class="flex-container flexFlowColumn overflowYAuto flex1">
5765 <div id="rm_group_add_members_header">
5766 <input id="rm_group_filter" class="text_pole margin0" type="search" data-i18n="[placeholder]Search..." placeholder="Search..." />
5767 </div>5759 </div>
5768 <div class="rm_tag_controls">5760 </div>
5769 <div class="tags rm_tag_filter"></div>5761 </div>
5762 <div class="inline-drawer wide100p flexFlowColumn">
5763 <div id="groupAddMemberListToggle" class="inline-drawer-toggle inline-drawer-header">
5764 <span data-i18n="Add Members">Add Members</span>
5765 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
5766 </div>
5767 <div class="inline-drawer-content">
5768 <div name="Unadded Char List" class="flex-container flexFlowColumn overflowYAuto flex1">
5769 <div id="rm_group_add_members_header">
5770 <input id="rm_group_filter" class="text_pole margin0" type="search" data-i18n="[placeholder]Search..." placeholder="Search..." />
5771 </div>
5772 <div class="rm_tag_controls">
5773 <div class="tags rm_tag_filter"></div>
5774 </div>
5775 <div id="rm_group_add_members_pagination" class="group_pagination"></div>
5776 <div id="rm_group_add_members" class="overflowYAuto flex-container" no_characters_text="No characters available" data-i18n="[no_characters_text]No characters available"></div>
5770 </div>5777 </div>
5771 <div id="rm_group_add_members_pagination" class="group_pagination"></div>
5772 <div id="rm_group_add_members" class="overflowYAuto flex-container" no_characters_text="No characters available" data-i18n="[no_characters_text]No characters available"></div>
5773 </div>5778 </div>
5774 </div>5779 </div>
5775 </div>5780 </div>
5776 </div>5781 <div id="rm_character_import" class="right_menu" style="display: none;">
5777 <div id="rm_character_import" class="right_menu" style="display: none;">5782 <form id="form_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">
5778 <form id="form_import" action="javascript:void(null);" method="post" enctype="multipart/form-data">5783 <input multiple type="file" id="character_import_file" accept=".json, image/png, .yaml, .yml, .charx" name="avatar">
5779 <input multiple type="file" id="character_import_file" accept=".json, image/png, .yaml, .yml, .charx" name="avatar">5784 <input id="character_import_file_type" name="file_type" class="text_pole" value="" autocomplete="off">
5780 <input id="character_import_file_type" name="file_type" class="text_pole" value="" autocomplete="off">
5781 </form>
5782 <input type="file" id="character_replace_file" accept=".json, image/png, .yaml, .yml, .charx" name="replace_avatar" hidden>
5783 </div>
5784 <div name="Character List Panel" id="rm_characters_block" class="right_menu">
5785 <div id="charListFixedTop">
5786 <form id="form_character_search_form" action="javascript:void(null);">
5787 <div id="rm_button_create" title="Create New Character" data-i18n="[title]Create New Character" class="menu_button fa-solid fa-user-plus "></div>
5788 <div id="character_import_button" title="Import Character from File" data-i18n="[title]Import Character from File" class="menu_button fa-solid fa-file-import faSmallFontSquareFix"></div>
5789 <div id="external_import_button" title="Import content from external URL" data-i18n="[title]Import content from external URL" class="menu_button fa-solid fa-cloud-arrow-down faSmallFontSquareFix"></div>
5790 <div id="rm_button_group_chats" title="Create New Chat Group" data-i18n="[title]Create New Chat Group" class="menu_button fa-solid fa-users-gear "></div>
5791 <input id="character_search_bar" class="text_pole width100p" type="search" data-i18n="[placeholder]Search..." placeholder="Search..." />
5792 <select id="character_sort_order" title="Characters sorting order" data-i18n="[title]Characters sorting order">
5793 <option data-field="search" data-order="desc" data-i18n="Search" hidden>Search</option>
5794 <option data-field="name" data-order="asc" data-i18n="A-Z">A-Z</option>
5795 <option data-field="name" data-order="desc" data-i18n="Z-A">Z-A</option>
5796 <option data-field="create_date" data-order="desc" data-i18n="Newest">Newest</option>
5797 <option data-field="create_date" data-order="asc" data-i18n="Oldest">Oldest</option>
5798 <option data-field="fav" data-order="desc" data-rule="boolean" data-i18n="Favorites">Favorites</option>
5799 <option data-field="date_last_chat" data-order="desc" data-i18n="Recent">Recent</option>
5800 <option data-field="chat_size" data-order="desc" data-i18n="Most chats">Most chats</option>
5801 <option data-field="chat_size" data-order="asc" data-i18n="Least chats">Least chats</option>
5802 <option data-field="data_size" data-order="desc" data-i18n="Most tokens">Most tokens</option>
5803 <option data-field="data_size" data-order="asc" data-i18n="Least tokens">Least tokens</option>
5804 <option data-field="name" data-order="random" data-i18n="Random">Random</option>
5805 </select>
5806 </form>5785 </form>
5807 <div class="rm_tag_controls">5786 <input type="file" id="character_replace_file" accept=".json, image/png, .yaml, .yml, .charx" name="replace_avatar" hidden>
5808 <div class="tags rm_tag_filter"></div>
5809 <div class="tags rm_tag_bogus_drilldown"></div>
5810 </div>
5811 <hr>
5812 </div>5787 </div>
5788 <div name="Character List Panel" id="rm_characters_block" class="right_menu">
5789 <div id="charListFixedTop">
5790 <form id="form_character_search_form" action="javascript:void(null);">
5791 <div id="rm_button_create" title="Create New Character" data-i18n="[title]Create New Character" class="menu_button fa-solid fa-user-plus "></div>
5792 <div id="character_import_button" title="Import Character from File" data-i18n="[title]Import Character from File" class="menu_button fa-solid fa-file-import faSmallFontSquareFix"></div>
5793 <div id="external_import_button" title="Import content from external URL" data-i18n="[title]Import content from external URL" class="menu_button fa-solid fa-cloud-arrow-down faSmallFontSquareFix"></div>
5794 <div id="rm_button_group_chats" title="Create New Chat Group" data-i18n="[title]Create New Chat Group" class="menu_button fa-solid fa-users-gear "></div>
5795 <input id="character_search_bar" class="text_pole width100p" type="search" data-i18n="[placeholder]Search..." placeholder="Search..." />
5796 <select id="character_sort_order" title="Characters sorting order" data-i18n="[title]Characters sorting order">
5797 <option data-field="search" data-order="desc" data-i18n="Search" hidden>Search</option>
5798 <option data-field="name" data-order="asc" data-i18n="A-Z">A-Z</option>
5799 <option data-field="name" data-order="desc" data-i18n="Z-A">Z-A</option>
5800 <option data-field="create_date" data-order="desc" data-i18n="Newest">Newest</option>
5801 <option data-field="create_date" data-order="asc" data-i18n="Oldest">Oldest</option>
5802 <option data-field="fav" data-order="desc" data-rule="boolean" data-i18n="Favorites">Favorites</option>
5803 <option data-field="date_last_chat" data-order="desc" data-i18n="Recent">Recent</option>
5804 <option data-field="chat_size" data-order="desc" data-i18n="Most chats">Most chats</option>
5805 <option data-field="chat_size" data-order="asc" data-i18n="Least chats">Least chats</option>
5806 <option data-field="data_size" data-order="desc" data-i18n="Most tokens">Most tokens</option>
5807 <option data-field="data_size" data-order="asc" data-i18n="Least tokens">Least tokens</option>
5808 <option data-field="name" data-order="random" data-i18n="Random">Random</option>
5809 </select>
5810 </form>
5811 <div class="rm_tag_controls">
5812 <div class="tags rm_tag_filter"></div>
5813 <div class="tags rm_tag_bogus_drilldown"></div>
5814 </div>
5815 <hr>
5816 </div>
58135817
5814 <div id="rm_print_characters_pagination">5818 <div id="rm_print_characters_pagination">
5815 <i id="charListGridToggle" class="fa-solid fa-table-cells-large menu_button" title="Toggle character grid view" data-i18n="[title]Toggle character grid view"></i>5819 <i id="charListGridToggle" class="fa-solid fa-table-cells-large menu_button" title="Toggle character grid view" data-i18n="[title]Toggle character grid view"></i>
5816 <i id="bulkEditButton" class="fa-solid fa-edit menu_button bulkEditButton" title="Bulk edit characters&#13;&#13;Click to toggle characters&#13;Shift + Click to select/deselect a range of characters&#13;Right-click for actions" data-i18n="[title]Bulk_edit_characters"></i>5820 <i id="bulkEditButton" class="fa-solid fa-edit menu_button bulkEditButton" title="Bulk edit characters&#13;&#13;Click to toggle characters&#13;Shift + Click to select/deselect a range of characters&#13;Right-click for actions" data-i18n="[title]Bulk_edit_characters"></i>
5817 <div id="bulkSelectedCount" class="bulkEditOptionElement paginationjs-nav"></div>5821 <div id="bulkSelectedCount" class="bulkEditOptionElement paginationjs-nav"></div>
5818 <i id="bulkSelectAllButton" class="fa-solid fa-check-double menu_button bulkEditOptionElement bulkSelectAllButton" title="Bulk select all characters" data-i18n="[title]Bulk select all characters" style="display: none;"></i>5822 <i id="bulkSelectAllButton" class="fa-solid fa-check-double menu_button bulkEditOptionElement bulkSelectAllButton" title="Bulk select all characters" data-i18n="[title]Bulk select all characters" style="display: none;"></i>
5819 <i id="bulkDeleteButton" class="fa-solid fa-trash menu_button bulkEditOptionElement bulkDeleteButton" title="Bulk delete characters" data-i18n="[title]Bulk delete characters" style="display: none;"></i>5823 <i id="bulkDeleteButton" class="fa-solid fa-trash menu_button bulkEditOptionElement bulkDeleteButton" title="Bulk delete characters" data-i18n="[title]Bulk delete characters" style="display: none;"></i>
5824 </div>
5825 <div id="rm_print_characters_block" class="flexFlowColumn"></div>
5820 </div>5826 </div>
5821 <div id="rm_print_characters_block" class="flexFlowColumn"></div>
5822 </div>5827 </div>
5823 </nav>5828 </nav>
5824 </div>5829 </div>
@@ -6195,6 +6200,298 @@
6195 </div>6200 </div>
6196 </div>6201 </div>
6197 <div id="entry_edit_template" class="template_element">6202 <div id="entry_edit_template" class="template_element">
6203 <div class="world_entry_edit">
6204 <div class="flex-container wide100p alignitemscenter">
6205 <div name="keywordsAndLogicBlock" class="flex-container wide100p alignitemscenter">
6206 <div class="world_entry_form_control keyprimary flex1">
6207 <small class="displayNone">
6208 <span data-i18n="Comma separated (required)">
6209 Comma separated (required)
6210 </span>
6211 </small>
6212 <small class="textAlignCenter" data-i18n="Primary Keywords">Primary Keywords</small>
6213 <select class="keyprimaryselect keyselect select2_multi_sameline" name="key" data-i18n="[placeholder]Keywords or Regexes" placeholder="Keywords or Regexes" multiple="multiple"></select>
6214 <textarea class="text_pole keyprimarytextpole mobile" name="key" rows="1" data-i18n="[placeholder]Comma separated list" placeholder="Comma separated list" style="display: none;"></textarea>
6215 <button type="button" class="switch_input_type_icon" tabindex="-1" title="Switch to plaintext mode" data-i18n="[title]Switch to plaintext mode" data-icon-on="✨" data-icon-off="⌨️" data-tooltip-on="Switch to fancy mode" data-tooltip-off="Switch to plaintext mode">
6216 ⌨️
6217 </button>
6218 </div>
6219 <div class="world_entry_form_control">
6220 <small class="textAlignCenter" data-i18n="Logic">Logic</small>
6221 <select name="entryLogicType" class="text_pole widthFitContent margin0">
6222 <option value="0" data-i18n="AND ANY">AND ANY</option>
6223 <option value="3" data-i18n="AND ALL">AND ALL</option>
6224 <option value="1" data-i18n="NOT ALL">NOT ALL</option>
6225 <option value="2" data-i18n="NOT ANY">NOT ANY</option>
6226 </select>
6227 </div>
6228 <div class="world_entry_form_control keysecondary flex1">
6229 <small class="displayNone">
6230 <span data-i18n="(ignored if empty)">
6231 (ignored if empty)
6232 </span>
6233 </small>
6234 <small class="textAlignCenter" data-i18n="Optional Filter">Optional Filter</small>
6235 <select class="keysecondaryselect keyselect select2_multi_sameline" name="keysecondary" data-i18n="[placeholder]Keywords or Regexes (ignored if empty)" placeholder="Keywords or Regexes (ignored if empty)" multiple="multiple"></select>
6236 <textarea class="text_pole keysecondarytextpole mobile" name="keysecondary" rows="1" data-i18n="[placeholder]Comma separated list (ignored if empty)" placeholder="Comma separated list (ignored if empty)" style="display: none;"></textarea>
6237 <button type="button" class="switch_input_type_icon" tabindex="-1" title="Switch to plaintext mode" data-i18n="[title]Switch to plaintext mode" data-icon-on="✨" data-icon-off="⌨️" data-tooltip-on="Switch to fancy mode" data-tooltip-off="Switch to plaintext mode">
6238 ⌨️
6239 </button>
6240 </div>
6241 </div>
6242 <div name="perEntryOverridesBlock" class="flex-container wide100p alignitemscenter">
6243 <div class="world_entry_form_control flex1">
6244 <small class="textAlignCenter" data-i18n="Scan Depth">Scan Depth</small>
6245 <input class="text_pole margin0" name="scanDepth" type="number" placeholder="Use global setting" data-i18n="[placeholder]Use global setting" max="1000">
6246 </div>
6247 <div class="world_entry_form_control flex1">
6248 <small class="textAlignCenter" data-i18n="Case-Sensitive">Case-Sensitive</small>
6249 <select name="caseSensitive" class="text_pole widthNatural margin0">
6250 <option value="null" data-i18n="Use global">Use global</option>
6251 <option value="true" data-i18n="Yes">Yes</option>
6252 <option value="false" data-i18n="No">No</option>
6253 </select>
6254 </div>
6255 <div class="world_entry_form_control flex1">
6256 <small class="textAlignCenter" data-i18n="Whole Words">Whole Words</small>
6257 <select name="matchWholeWords" class="text_pole widthNatural margin0">
6258 <option value="null" data-i18n="Use global">Use global</option>
6259 <option value="true" data-i18n="Yes">Yes</option>
6260 <option value="false" data-i18n="No">No</option>
6261 </select>
6262 </div>
6263 <div class="world_entry_form_control flex1">
6264 <small class="textAlignCenter" data-i18n="Group Scoring">Group Scoring</small>
6265 <select name="useGroupScoring" class="text_pole widthNatural margin0">
6266 <option value="null" data-i18n="Use global">Use global</option>
6267 <option value="true" data-i18n="Yes">Yes</option>
6268 <option value="false" data-i18n="No">No</option>
6269 </select>
6270 </div>
6271 <div class="world_entry_form_control flex1" title="Can be used to automatically activate Quick Replies" data-i18n="[title]Can be used to automatically activate Quick Replies">
6272 <small class="textAlignCenter" data-i18n="Automation ID">Automation ID</small>
6273 <input class="text_pole margin0" name="automationId" type="text" placeholder="( None )" data-i18n="[placeholder]( None )">
6274 </div>
6275 <div class="world_entry_form_control flex1" title="Defines delay levels for recursive scans.&#13;&#13;Initially, only the first level (smallest number) will match.&#13;Once no matches are found, the next level becomes eligible for matching.&#13;This repeats until all levels are checked.&#13;&#13;Tied to the &quot;Delay until recursion&quot; setting." data-i18n="[title]delay_until_recursion_level">
6276 <small class="textAlignCenter">
6277 <span data-i18n="Recursion Level">Recursion Level</span>
6278 <div class="fa-solid fa-circle-info opacity50p"></div>
6279 </small>
6280 <input class="text_pole margin0" name="delayUntilRecursionLevel" type="text" placeholder="1">
6281 </div>
6282 </div>
6283 <div name="contentAndCharFilterBlock" class="world_entry_thin_controls flex2">
6284 <div class="world_entry_form_control flex1">
6285 <label for="content ">
6286 <small>
6287 <span class="alignitemscenter flex-container flexnowrap wide100p justifySpaceBetween">
6288 <span class="alignitemscenter flex-container">
6289 <span data-i18n="Content" class="mdhotkey_location">
6290 Content
6291 </span>
6292 <i class="editor_maximize fa-solid fa-maximize right_menu_button" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
6293 </span>
6294 <span>
6295 (<span data-i18n="extension_token_counter">Tokens:</span>&nbsp; <span class="world_entry_form_token_counter" data-first-run="true">counting...</span>)&nbsp;
6296 <span class="world_entry_form_uid_value" data-first-run="true"></span>
6297 </span>
6298 <div>
6299 <label class="checkbox flex-container alignitemscenter flexNoGap">
6300 <input type="checkbox" name="excludeRecursion" />
6301 <span data-i18n="Non-recursable (will not be activated by another)">
6302 Non-recursable (will not be activated by another)
6303 </span>
6304 </label>
6305 <label class="checkbox flex-container alignitemscenter flexNoGap">
6306 <input type="checkbox" name="preventRecursion" />
6307 <span data-i18n="Prevent further recursion (this entry will not activate others)">
6308 Prevent further recursion (will not activate others)
6309 </span>
6310 </label>
6311 <label class="checkbox flex-container alignitemscenter flexNoGap">
6312 <input type="checkbox" name="delay_until_recursion" />
6313 <span data-i18n="Delay until recursion (can only be activated on recursive checking)">
6314 Delay until recursion (can only be activated on recursive checking)
6315 </span>
6316 </label>
6317 </div>
6318 </span>
6319 </small>
6320 <small class="displayNone">
6321 <span data-i18n="What this keyword should mean to the AI, sent verbatim">
6322 What this keyword should mean to the AI, sent verbatim
6323 </span>
6324 </small>
6325 </label>
6326 <textarea class="text_pole mdHotkeys" name="content" rows="8" data-i18n="[placeholder]What this keyword should mean to the AI, sent verbatim" placeholder="What this keyword should mean to the AI, sent verbatim"></textarea>
6327 </div>
6328 </div>
6329 <div class="world_entry_thin_controls commentContainer">
6330 </div>
6331 </div>
6332 <div class="flex-container wide100p flexGap10">
6333 <div class="flex4 flex-container flexFlowColumn flexNoGap">
6334 <div class="flex-container justifySpaceBetween">
6335 <small for="group">
6336 <span data-i18n="Inclusion Group">Inclusion Group</span>
6337 <a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/#inclusion-group" class="notes-link" target="_blank" title="Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.&#13;Supports multiple comma-separated groups.&#13;&#13;Documentation: World Info - Inclusion Group" data-i18n="[title]Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group">
6338 <span class="fa-solid fa-circle-question note-link-span"></span>
6339 </a>
6340 </small>
6341 <label class="checkbox_label flexNoGap margin-r5" for="groupOverride">
6342 <input type="checkbox" name="groupOverride" />
6343 <span>
6344 <small title="Prioritize this entry: When checked, this entry is prioritized out of all selections.&#13;If multiple are prioritized, the one with the highest 'Order' is chosen.&#13;" data-i18n="[title]Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.">
6345 <span data-i18n="Prioritize">Prioritize</span>
6346 <div class="fa-solid fa-circle-info opacity50p"></div>
6347 </small>
6348 </span>
6349 </label>
6350 </div>
6351 <div class="range-block-range">
6352 <input type="text" class="text_pole margin0" name="group" rows="1" data-i18n="[placeholder]Only one entry with the same label will be activated" placeholder="Only one entry with the same label will be activated">
6353 </div>
6354 </div>
6355 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]A relative likelihood of entry activation within the group" title="A relative likelihood of entry activation within the group">
6356 <div class="flex-container justifySpaceBetween marginBot5">
6357 <small for="groupWeight" data-i18n="Group Weight">
6358 Group Weight
6359 </small>
6360 </div>
6361 <div class="range-block-range">
6362 <input type="number" class="text_pole margin0" name="groupWeight" rows="1" placeholder="100" min="1" max="999999">
6363 </div>
6364 </div>
6365 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Sticky entries will stay active for N messages after being triggered." title="Sticky entries will stay active for N messages after being triggered.">
6366 <div class="flex-container justifySpaceBetween marginBot5">
6367 <small class="flex-container alignItemsBaseline" for="sticky">
6368 <span data-i18n="Sticky">
6369 Sticky
6370 </span>
6371 <i class="fa-solid fa-comments fa-xs"></i>
6372 </small>
6373 </div>
6374 <div class="range-block-range">
6375 <input class="text_pole margin0" name="sticky" type="number" data-i18n="[placeholder]Non-sticky" placeholder="Non-sticky" min="0" max="999999">
6376 </div>
6377 </div>
6378 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
6379 <div class="flex-container justifySpaceBetween marginBot5">
6380 <small class="flex-container alignItemsBaseline" for="cooldown">
6381 <span data-i18n="Cooldown">
6382 Cooldown
6383 </span>
6384 <i class="fa-solid fa-comments fa-xs"></i>
6385 </small>
6386 </div>
6387 <div class="range-block-range">
6388 <input class="text_pole margin0" name="cooldown" type="number" data-i18n="[placeholder]No cooldown" placeholder="No cooldown" min="0" max="999999">
6389 </div>
6390 </div>
6391 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
6392 <div class="flex-container justifySpaceBetween marginBot5">
6393 <small class="flex-container alignItemsBaseline" for="delay">
6394 <span data-i18n="Delay">
6395 Delay
6396 </span>
6397 <i class="fa-solid fa-comments fa-xs"></i>
6398 </small>
6399 </div>
6400 <div class="range-block-range">
6401 <input class="text_pole margin0" name="delay" type="number" data-i18n="[placeholder]No delay" placeholder="No delay" min="0" max="999999">
6402 </div>
6403 </div>
6404 </div>
6405 <div class="flex-container wide100p flexGap10">
6406 <div class="flex4 flex-container flexFlowColumn flexNoGap">
6407 <div class="flex-container justifySpaceBetween">
6408 <small for="characterFilter" data-i18n="Filter to Characters or Tags">
6409 Filter to Characters or Tags
6410 </small>
6411 <label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">
6412 <input type="checkbox" name="character_exclusion" />
6413 <span>
6414 <small title="Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry" data-i18n="[title]Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry">
6415 <span data-i18n="Exclude">Exclude</span>
6416 <div class="fa-solid fa-circle-info opacity50p"></div>
6417 </small>
6418 </span>
6419 </label>
6420 </div>
6421 <div class="range-block-range">
6422 <select name="characterFilter" class="select2_multi_sameline" multiple>
6423 <option value="" data-i18n="-- Characters not found --">
6424 -- Characters not found --
6425 </option>
6426 </select>
6427 </div>
6428 </div>
6429 </div>
6430 <div name="WIEntryBottomControls" class="flex-container flex1 justifySpaceBetween world_entry_form_horizontal">
6431 <div class="flex-container flexFlowColumn flexNoGap wi-enter-footer-text">
6432 <label class="checkbox flex-container">
6433 <input type="checkbox" name="selective" />
6434 <span data-i18n="Selective">Selective</span>
6435 </label>
6436 <label class="checkbox flex-container">
6437 <input type="checkbox" name="useProbability" />
6438 <span data-i18n="Use Probability">Use Probability</span>
6439 </label>
6440 <label class="checkbox flex-container">
6441 <input type="checkbox" name="addMemo">
6442 <span data-i18n="Add Memo">Add Memo</span>
6443 </label>
6444 </div>
6445 </div>
6446 <div class="inline-drawer wide100p flexFlowColumn">
6447 <div class="inline-drawer-toggle inline-drawer-header inline-drawer-header-pointer userSettingsInnerExpandable">
6448 <strong data-i18n="Additional Matching Sources">Additional Matching Sources</strong>
6449 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
6450 </div>
6451 <div class="inline-drawer-content flex-container flexFlowRow flexGap10 paddingBottom5px">
6452 <small class="flex-container flex1 flexFlowColumn">
6453 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6454 <input type="checkbox" name="matchCharacterDescription" />
6455 <span data-i18n="Character Description">
6456 Character Description
6457 </span>
6458 </label>
6459 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6460 <input type="checkbox" name="matchCharacterPersonality" />
6461 <span data-i18n="Character Personality">
6462 Character Personality
6463 </span>
6464 </label>
6465 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6466 <input type="checkbox" name="matchScenario" />
6467 <span data-i18n="Scenario">
6468 Scenario
6469 </span>
6470 </label>
6471 </small>
6472 <small class="flex-container flex1 flexFlowColumn">
6473 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6474 <input type="checkbox" name="matchPersonaDescription" />
6475 <span data-i18n="Persona Description">
6476 Persona Description
6477 </span>
6478 </label>
6479 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6480 <input type="checkbox" name="matchCharacterDepthPrompt" />
6481 <span data-i18n="Character's Note">
6482 Character's Note
6483 </span>
6484 </label>
6485 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6486 <input type="checkbox" name="matchCreatorNotes" />
6487 <span data-i18n="Creator's Notes">
6488 Creator's Notes
6489 </span>
6490 </label>
6491 </small>
6492 </div>
6493 </div>
6494 </div>
6198 <div class="world_entry">6495 <div class="world_entry">
6199 <form class="world_entry_form wi-card-entry">6496 <form class="world_entry_form wi-card-entry">
6200 <div class="inline-drawer wide100p">6497 <div class="inline-drawer wide100p">
@@ -6267,297 +6564,8 @@
6267 <i class="menu_button duplicate_entry_button fa-solid fa-paste" title="Duplicate world info entry" data-i18n="[title]Duplicate world info entry" type="submit" value=""></i>6564 <i class="menu_button duplicate_entry_button fa-solid fa-paste" title="Duplicate world info entry" data-i18n="[title]Duplicate world info entry" type="submit" value=""></i>
6268 <i class="menu_button delete_entry_button fa-solid fa-trash-can" title="Delete world info entry" data-i18n="[title]Delete world info entry" type="submit" value=""></i>6565 <i class="menu_button delete_entry_button fa-solid fa-trash-can" title="Delete world info entry" data-i18n="[title]Delete world info entry" type="submit" value=""></i>
6269 </div>6566 </div>
6270 <div class="inline-drawer-content flex-container paddingBottom5px wide100p">6567 <div class="inline-drawer-content inline-drawer-outlet flex-container paddingBottom5px wide100p ">
6271 <div class="flex-container wide100p alignitemscenter">6568 <!-- Outlet for world_entry_edit -->
6272 <div name="keywordsAndLogicBlock" class="flex-container wide100p alignitemscenter">
6273 <div class="world_entry_form_control keyprimary flex1">
6274 <small class="displayNone">
6275 <span data-i18n="Comma separated (required)">
6276 Comma separated (required)
6277 </span>
6278 </small>
6279 <small class="textAlignCenter" data-i18n="Primary Keywords">Primary Keywords</small>
6280 <select class="keyprimaryselect keyselect select2_multi_sameline" name="key" data-i18n="[placeholder]Keywords or Regexes" placeholder="Keywords or Regexes" multiple="multiple"></select>
6281 <textarea class="text_pole keyprimarytextpole mobile" name="key" rows="1" data-i18n="[placeholder]Comma separated list" placeholder="Comma separated list" style="display: none;"></textarea>
6282 <button type="button" class="switch_input_type_icon" tabindex="-1" title="Switch to plaintext mode" data-i18n="[title]Switch to plaintext mode" data-icon-on="✨" data-icon-off="⌨️" data-tooltip-on="Switch to fancy mode" data-tooltip-off="Switch to plaintext mode">
6283 ⌨️
6284 </button>
6285 </div>
6286 <div class="world_entry_form_control">
6287 <small class="textAlignCenter" data-i18n="Logic">Logic</small>
6288 <select name="entryLogicType" class="text_pole widthFitContent margin0">
6289 <option value="0" data-i18n="AND ANY">AND ANY</option>
6290 <option value="3" data-i18n="AND ALL">AND ALL</option>
6291 <option value="1" data-i18n="NOT ALL">NOT ALL</option>
6292 <option value="2" data-i18n="NOT ANY">NOT ANY</option>
6293 </select>
6294 </div>
6295 <div class="world_entry_form_control keysecondary flex1">
6296 <small class="displayNone">
6297 <span data-i18n="(ignored if empty)">
6298 (ignored if empty)
6299 </span>
6300 </small>
6301 <small class="textAlignCenter" data-i18n="Optional Filter">Optional Filter</small>
6302 <select class="keysecondaryselect keyselect select2_multi_sameline" name="keysecondary" data-i18n="[placeholder]Keywords or Regexes (ignored if empty)" placeholder="Keywords or Regexes (ignored if empty)" multiple="multiple"></select>
6303 <textarea class="text_pole keysecondarytextpole mobile" name="keysecondary" rows="1" data-i18n="[placeholder]Comma separated list (ignored if empty)" placeholder="Comma separated list (ignored if empty)" style="display: none;"></textarea>
6304 <button type="button" class="switch_input_type_icon" tabindex="-1" title="Switch to plaintext mode" data-i18n="[title]Switch to plaintext mode" data-icon-on="✨" data-icon-off="⌨️" data-tooltip-on="Switch to fancy mode" data-tooltip-off="Switch to plaintext mode">
6305 ⌨️
6306 </button>
6307 </div>
6308 </div>
6309 <div name="perEntryOverridesBlock" class="flex-container wide100p alignitemscenter">
6310 <div class="world_entry_form_control flex1">
6311 <small class="textAlignCenter" data-i18n="Scan Depth">Scan Depth</small>
6312 <input class="text_pole margin0" name="scanDepth" type="number" placeholder="Use global setting" data-i18n="[placeholder]Use global setting" max="1000">
6313 </div>
6314 <div class="world_entry_form_control flex1">
6315 <small class="textAlignCenter" data-i18n="Case-Sensitive">Case-Sensitive</small>
6316 <select name="caseSensitive" class="text_pole widthNatural margin0">
6317 <option value="null" data-i18n="Use global">Use global</option>
6318 <option value="true" data-i18n="Yes">Yes</option>
6319 <option value="false" data-i18n="No">No</option>
6320 </select>
6321 </div>
6322 <div class="world_entry_form_control flex1">
6323 <small class="textAlignCenter" data-i18n="Whole Words">Whole Words</small>
6324 <select name="matchWholeWords" class="text_pole widthNatural margin0">
6325 <option value="null" data-i18n="Use global">Use global</option>
6326 <option value="true" data-i18n="Yes">Yes</option>
6327 <option value="false" data-i18n="No">No</option>
6328 </select>
6329 </div>
6330 <div class="world_entry_form_control flex1">
6331 <small class="textAlignCenter" data-i18n="Group Scoring">Group Scoring</small>
6332 <select name="useGroupScoring" class="text_pole widthNatural margin0">
6333 <option value="null" data-i18n="Use global">Use global</option>
6334 <option value="true" data-i18n="Yes">Yes</option>
6335 <option value="false" data-i18n="No">No</option>
6336 </select>
6337 </div>
6338 <div class="world_entry_form_control flex1" title="Can be used to automatically activate Quick Replies" data-i18n="[title]Can be used to automatically activate Quick Replies">
6339 <small class="textAlignCenter" data-i18n="Automation ID">Automation ID</small>
6340 <input class="text_pole margin0" name="automationId" type="text" placeholder="( None )" data-i18n="[placeholder]( None )">
6341 </div>
6342 <div class="world_entry_form_control flex1" title="Defines delay levels for recursive scans.&#13;&#13;Initially, only the first level (smallest number) will match.&#13;Once no matches are found, the next level becomes eligible for matching.&#13;This repeats until all levels are checked.&#13;&#13;Tied to the &quot;Delay until recursion&quot; setting." data-i18n="[title]delay_until_recursion_level">
6343 <small class="textAlignCenter">
6344 <span data-i18n="Recursion Level">Recursion Level</span>
6345 <div class="fa-solid fa-circle-info opacity50p"></div>
6346 </small>
6347 <input class="text_pole margin0" name="delayUntilRecursionLevel" type="text" placeholder="1">
6348 </div>
6349 </div>
6350 <div name="contentAndCharFilterBlock" class="world_entry_thin_controls flex2">
6351 <div class="world_entry_form_control flex1">
6352 <label for="content ">
6353 <small>
6354 <span class="alignitemscenter flex-container flexnowrap wide100p justifySpaceBetween">
6355 <span class="alignitemscenter flex-container">
6356 <span data-i18n="Content" class="mdhotkey_location">
6357 Content
6358 </span>
6359 <i class="editor_maximize fa-solid fa-maximize right_menu_button" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
6360 </span>
6361 <span>
6362 (<span data-i18n="extension_token_counter">Tokens:</span>&nbsp; <span class="world_entry_form_token_counter" data-first-run="true">counting...</span>)&nbsp;
6363 <span class="world_entry_form_uid_value" data-first-run="true"></span>
6364 </span>
6365 <div>
6366 <label class="checkbox flex-container alignitemscenter flexNoGap">
6367 <input type="checkbox" name="exclude_recursion" />
6368 <span data-i18n="Non-recursable (will not be activated by another)">
6369 Non-recursable (will not be activated by another)
6370 </span>
6371 </label>
6372 <label class="checkbox flex-container alignitemscenter flexNoGap">
6373 <input type="checkbox" name="prevent_recursion" />
6374 <span data-i18n="Prevent further recursion (this entry will not activate others)">
6375 Prevent further recursion (will not activate others)
6376 </span>
6377 </label>
6378 <label class="checkbox flex-container alignitemscenter flexNoGap">
6379 <input type="checkbox" name="delay_until_recursion" />
6380 <span data-i18n="Delay until recursion (can only be activated on recursive checking)">
6381 Delay until recursion (can only be activated on recursive checking)
6382 </span>
6383 </label>
6384 </div>
6385 </span>
6386 </small>
6387 <small class="displayNone">
6388 <span data-i18n="What this keyword should mean to the AI, sent verbatim">
6389 What this keyword should mean to the AI, sent verbatim
6390 </span>
6391 </small>
6392 </label>
6393 <textarea class="text_pole mdHotkeys" name="content" rows="8" data-i18n="[placeholder]What this keyword should mean to the AI, sent verbatim" placeholder="What this keyword should mean to the AI, sent verbatim"></textarea>
6394 </div>
6395 </div>
6396 <div class="world_entry_thin_controls commentContainer">
6397 </div>
6398 </div>
6399 <div class="flex-container wide100p flexGap10">
6400 <div class="flex4 flex-container flexFlowColumn flexNoGap">
6401 <div class="flex-container justifySpaceBetween">
6402 <small for="group">
6403 <span data-i18n="Inclusion Group">Inclusion Group</span>
6404 <a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/#inclusion-group" class="notes-link" target="_blank" title="Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.&#13;Supports multiple comma-separated groups.&#13;&#13;Documentation: World Info - Inclusion Group" data-i18n="[title]Inclusion Groups ensure only one entry from a group is activated at a time, if multiple are triggered.Documentation: World Info - Inclusion Group">
6405 <span class="fa-solid fa-circle-question note-link-span"></span>
6406 </a>
6407 </small>
6408 <label class="checkbox_label flexNoGap margin-r5" for="groupOverride">
6409 <input type="checkbox" name="groupOverride" />
6410 <span>
6411 <small title="Prioritize this entry: When checked, this entry is prioritized out of all selections.&#13;If multiple are prioritized, the one with the highest 'Order' is chosen.&#13;" data-i18n="[title]Prioritize this entry: When checked, this entry is prioritized out of all selections.If multiple are prioritized, the one with the highest 'Order' is chosen.">
6412 <span data-i18n="Prioritize">Prioritize</span>
6413 <div class="fa-solid fa-circle-info opacity50p"></div>
6414 </small>
6415 </span>
6416 </label>
6417 </div>
6418 <div class="range-block-range">
6419 <input type="text" class="text_pole margin0" name="group" rows="1" data-i18n="[placeholder]Only one entry with the same label will be activated" placeholder="Only one entry with the same label will be activated">
6420 </div>
6421 </div>
6422 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]A relative likelihood of entry activation within the group" title="A relative likelihood of entry activation within the group">
6423 <div class="flex-container justifySpaceBetween marginBot5">
6424 <small for="groupWeight" data-i18n="Group Weight">
6425 Group Weight
6426 </small>
6427 </div>
6428 <div class="range-block-range">
6429 <input type="number" class="text_pole margin0" name="groupWeight" rows="1" placeholder="100" min="1" max="999999">
6430 </div>
6431 </div>
6432 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Sticky entries will stay active for N messages after being triggered." title="Sticky entries will stay active for N messages after being triggered.">
6433 <div class="flex-container justifySpaceBetween marginBot5">
6434 <small class="flex-container alignItemsBaseline" for="sticky">
6435 <span data-i18n="Sticky">
6436 Sticky
6437 </span>
6438 <i class="fa-solid fa-comments fa-xs"></i>
6439 </small>
6440 </div>
6441 <div class="range-block-range">
6442 <input class="text_pole margin0" name="sticky" type="number" data-i18n="[placeholder]Non-sticky" placeholder="Non-sticky" min="0" max="999999">
6443 </div>
6444 </div>
6445 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a cooldown can't be activated N messages after being triggered." title="Entries with a cooldown can't be activated N messages after being triggered.">
6446 <div class="flex-container justifySpaceBetween marginBot5">
6447 <small class="flex-container alignItemsBaseline" for="cooldown">
6448 <span data-i18n="Cooldown">
6449 Cooldown
6450 </span>
6451 <i class="fa-solid fa-comments fa-xs"></i>
6452 </small>
6453 </div>
6454 <div class="range-block-range">
6455 <input class="text_pole margin0" name="cooldown" type="number" data-i18n="[placeholder]No cooldown" placeholder="No cooldown" min="0" max="999999">
6456 </div>
6457 </div>
6458 <div class="flex2 flex-container flexFlowColumn flexNoGap" data-i18n="[title]Entries with a delay can't be activated until there are N messages present in the chat." title="Entries with a delay can't be activated until there are N messages present in the chat.">
6459 <div class="flex-container justifySpaceBetween marginBot5">
6460 <small class="flex-container alignItemsBaseline" for="delay">
6461 <span data-i18n="Delay">
6462 Delay
6463 </span>
6464 <i class="fa-solid fa-comments fa-xs"></i>
6465 </small>
6466 </div>
6467 <div class="range-block-range">
6468 <input class="text_pole margin0" name="delay" type="number" data-i18n="[placeholder]No delay" placeholder="No delay" min="0" max="999999">
6469 </div>
6470 </div>
6471 </div>
6472 <div class="flex-container wide100p flexGap10">
6473 <div class="flex4 flex-container flexFlowColumn flexNoGap">
6474 <div class="flex-container justifySpaceBetween">
6475 <small for="characterFilter" data-i18n="Filter to Characters or Tags">
6476 Filter to Characters or Tags
6477 </small>
6478 <label class="checkbox_label flexNoGap margin-r5" for="character_exclusion">
6479 <input type="checkbox" name="character_exclusion" />
6480 <span>
6481 <small title="Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry" data-i18n="[title]Switch the Character/Tags filter around to exclude the listed characters and tags from matching for this entry">
6482 <span data-i18n="Exclude">Exclude</span>
6483 <div class="fa-solid fa-circle-info opacity50p"></div>
6484 </small>
6485 </span>
6486 </label>
6487 </div>
6488 <div class="range-block-range">
6489 <select name="characterFilter" class="select2_multi_sameline" multiple>
6490 <option value="" data-i18n="-- Characters not found --">
6491 -- Characters not found --
6492 </option>
6493 </select>
6494 </div>
6495 </div>
6496 </div>
6497 <div name="WIEntryBottomControls" class="flex-container flex1 justifySpaceBetween world_entry_form_horizontal">
6498 <div class="flex-container flexFlowColumn flexNoGap wi-enter-footer-text ">
6499 <label class="checkbox flex-container">
6500 <input type="checkbox" name="selective" />
6501 <span data-i18n="Selective">Selective</span>
6502 </label>
6503 <label class="checkbox flex-container">
6504 <input type="checkbox" name="useProbability" />
6505 <span data-i18n="Use Probability">Use Probability</span>
6506 </label>
6507 <label class="checkbox flex-container">
6508 <input type="checkbox" name="addMemo">
6509 <span data-i18n="Add Memo">Add Memo</span>
6510 </label>
6511 </div>
6512 </div>
6513 <div class="inline-drawer wide100p flexFlowColumn">
6514 <div class="inline-drawer-toggle inline-drawer-header inline-drawer-header-pointer userSettingsInnerExpandable">
6515 <strong data-i18n="Additional Matching Sources">Additional Matching Sources</strong>
6516 <div class="fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
6517 </div>
6518 <div class="inline-drawer-content flex-container flexFlowRow flexGap10 paddingBottom5px">
6519 <small class="flex-container flex1 flexFlowColumn">
6520 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6521 <input type="checkbox" name="matchCharacterDescription" />
6522 <span data-i18n="Character Description">
6523 Character Description
6524 </span>
6525 </label>
6526 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6527 <input type="checkbox" name="matchCharacterPersonality" />
6528 <span data-i18n="Character Personality">
6529 Character Personality
6530 </span>
6531 </label>
6532 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6533 <input type="checkbox" name="matchScenario" />
6534 <span data-i18n="Scenario">
6535 Scenario
6536 </span>
6537 </label>
6538 </small>
6539 <small class="flex-container flex1 flexFlowColumn">
6540 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6541 <input type="checkbox" name="matchPersonaDescription" />
6542 <span data-i18n="Persona Description">
6543 Persona Description
6544 </span>
6545 </label>
6546 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6547 <input type="checkbox" name="matchCharacterDepthPrompt" />
6548 <span data-i18n="Character's Note">
6549 Character's Note
6550 </span>
6551 </label>
6552 <label class="checkbox flex-container alignItemsCenter flexNoGap">
6553 <input type="checkbox" name="matchCreatorNotes" />
6554 <span data-i18n="Creator's Notes">
6555 Creator's Notes
6556 </span>
6557 </label>
6558 </small>
6559 </div>
6560 </div>
6561 </div>6569 </div>
6562 </div>6570 </div>
6563 </form>6571 </form>
public/script.js+35 -85
@@ -10,7 +10,6 @@ import {
10 SVGInject,10 SVGInject,
11 Popper,11 Popper,
12 initLibraryShims,12 initLibraryShims,
13 slideToggle,
14 default as libs,13 default as libs,
15} from './lib.js';14} from './lib.js';
1615
@@ -10320,86 +10319,43 @@ function doDrawerOpenClick() {
10320/**10319/**
10321 * Event handler to open or close a navbar drawer when a navbar icon is clicked.10320 * Event handler to open or close a navbar drawer when a navbar icon is clicked.
10322 * Handles click events on .drawer-toggle elements.10321 * Handles click events on .drawer-toggle elements.
10323 * @returns {void}10322 * @returns {Promise<void>}
10324 */10323 */
10325function doNavbarIconClick() {10324export async function doNavbarIconClick() {
10326 const icon = $(this).find('.drawer-icon');10325 const icon = $(this).find('.drawer-icon');
10327 const drawer = $(this).parent().find('.drawer-content');10326 const drawer = $(this).parent().find('.drawer-content');
10328 if (drawer.hasClass('resizing')) { return; }
10329 const drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer');10327 const drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer');
10330 const targetDrawerID = $(this).parent().find('.drawer-content').attr('id');10328 const targetDrawerID = $(this).parent().find('.drawer-content').attr('id');
10331 const pinnedDrawerClicked = drawer.hasClass('pinnedOpen');10329
1033210330 if (!drawerWasOpenAlready) {
10333 if (!drawerWasOpenAlready) { //to open the drawer10331 const $openDrawers = $('.openDrawer:not(.pinnedOpen)');
10334 $('.openDrawer').not('.pinnedOpen').addClass('resizing').each((_, el) => {10332 const $openIcons = $('.openIcon:not(.drawerPinnedOpen)');
10335 slideToggle(el, {10333 for (const iconEl of $openIcons) {
10336 ...getSlideToggleOptions(),10334 $(iconEl).toggleClass('closedIcon openIcon');
10337 onAnimationEnd: function (el) {10335 }
10338 el.closest('.drawer-content').classList.remove('resizing');10336 for (const el of $openDrawers) {
10339 },10337 $(el).toggleClass('closedDrawer openDrawer');
10340 });10338 }
10341 });10339 if ($openDrawers.length) {
10342 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');10340 await delay(animation_duration);
10343 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');10341 }
10344 icon.toggleClass('openIcon closedIcon');10342 icon.toggleClass('openIcon closedIcon');
10345 drawer.toggleClass('openDrawer closedDrawer');10343 drawer.toggleClass('openDrawer closedDrawer');
1034610344
10347 //console.log(targetDrawerID);
10348 if (targetDrawerID === 'right-nav-panel') {10345 if (targetDrawerID === 'right-nav-panel') {
10349 $(this).closest('.drawer').find('.drawer-content').addClass('resizing').each((_, el) => {10346 favsToHotswap();
10350 slideToggle(el, {10347 $('#rm_print_characters_block').trigger('scroll');
10351 ...getSlideToggleOptions(),
10352 elementDisplayStyle: 'flex',
10353 onAnimationEnd: function (el) {
10354 el.closest('.drawer-content').classList.remove('resizing');
10355 favsToHotswap();
10356 $('#rm_print_characters_block').trigger('scroll');
10357 },
10358 });
10359 });
10360 } else {
10361 $(this).closest('.drawer').find('.drawer-content').addClass('resizing').each((_, el) => {
10362 slideToggle(el, {
10363 ...getSlideToggleOptions(),
10364 onAnimationEnd: function (el) {
10365 el.closest('.drawer-content').classList.remove('resizing');
10366 },
10367 });
10368 });
10369 }10348 }
1037010349
10371 // Set the height of "autoSetHeight" textareas within the drawer to their scroll height10350 // Set the height of "autoSetHeight" textareas within the drawer to their scroll height
10372 if (!CSS.supports('field-sizing', 'content')) {10351 if (!CSS.supports('field-sizing', 'content')) {
10373 $(this).closest('.drawer').find('.drawer-content textarea.autoSetHeight').each(async function () {10352 const textareas = $(this).closest('.drawer').find('.drawer-content textarea.autoSetHeight');
10374 await resetScrollHeight($(this));10353 for (const textarea of textareas) {
10375 return;10354 await resetScrollHeight($(textarea));
10376 });10355 }
10377 }10356 }
1037810357 } else if (drawerWasOpenAlready) {
10379 } else if (drawerWasOpenAlready) { //to close manually
10380 icon.toggleClass('closedIcon openIcon');10358 icon.toggleClass('closedIcon openIcon');
10381
10382 if (pinnedDrawerClicked) {
10383 $(drawer).addClass('resizing').each((_, el) => {
10384 slideToggle(el, {
10385 ...getSlideToggleOptions(),
10386 onAnimationEnd: function (el) {
10387 el.classList.remove('resizing');
10388 },
10389 });
10390 });
10391 }
10392 else {
10393 $('.openDrawer').not('.pinnedOpen').addClass('resizing').each((_, el) => {
10394 slideToggle(el, {
10395 ...getSlideToggleOptions(),
10396 onAnimationEnd: function (el) {
10397 el.closest('.drawer-content').classList.remove('resizing');
10398 },
10399 });
10400 });
10401 }
10402
10403 drawer.toggleClass('closedDrawer openDrawer');10359 drawer.toggleClass('closedDrawer openDrawer');
10404 }10360 }
10405}10361}
@@ -12028,8 +11984,8 @@ jQuery(async function () {
1202811984
12029 $('.drawer-toggle').on('click', doNavbarIconClick);11985 $('.drawer-toggle').on('click', doNavbarIconClick);
1203011986
12031 $('html').on('touchstart mousedown', function (e) {11987 $('html').on('touchstart mousedown', async function (e) {
12032 var clickTarget = $(e.target);11988 const clickTarget = $(e.target);
1203311989
12034 if (isExportPopupOpen11990 if (isExportPopupOpen
12035 && clickTarget.closest('#export_button').length == 011991 && clickTarget.closest('#export_button').length == 0
@@ -12050,28 +12006,21 @@ jQuery(async function () {
12050 '#toast-container',12006 '#toast-container',
12051 '.select2-results',12007 '.select2-results',
12052 ];12008 ];
12009
12053 for (const id of forbiddenTargets) {12010 for (const id of forbiddenTargets) {
12054 if (clickTarget.closest(id).length > 0) {12011 if (clickTarget.closest(id).length > 0) {
12055 return;12012 return;
12056 }12013 }
12057 }12014 }
1205812015
12059 var targetParentHasOpenDrawer = clickTarget.parents('.openDrawer').length;12016 // This autocloses open drawers that are not pinned if a click happens inside the app which does not target them.
12060 if (clickTarget.hasClass('drawer-icon') == false && !clickTarget.hasClass('openDrawer')) {12017 const targetParentHasOpenDrawer = clickTarget.parents('.openDrawer').length;
12061 if ($('.openDrawer').length !== 0) {12018 if (!clickTarget.hasClass('drawer-icon') && !clickTarget.hasClass('openDrawer')) {
12062 if (targetParentHasOpenDrawer === 0) {12019 const $openDrawers = $('.openDrawer').not('.pinnedOpen');
12063 //console.log($('.openDrawer').not('.pinnedOpen').length);12020 if ($openDrawers.length && targetParentHasOpenDrawer === 0) {
12064 $('.openDrawer').not('.pinnedOpen').addClass('resizing').each((_, el) => {12021 // Toggle icon and drawer classes
12065 slideToggle(el, {12022 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
12066 ...getSlideToggleOptions(),12023 $openDrawers.toggleClass('closedDrawer openDrawer');
12067 onAnimationEnd: (el) => {
12068 el.closest('.drawer-content').classList.remove('resizing');
12069 },
12070 });
12071 });
12072 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
12073 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');
12074 }
12075 }12024 }
12076 }12025 }
12077 });12026 });
@@ -12085,6 +12034,7 @@ jQuery(async function () {
12085 const drawerContent = drawer.find('>.inline-drawer-content');12034 const drawerContent = drawer.find('>.inline-drawer-content');
12086 icon.toggleClass('down up');12035 icon.toggleClass('down up');
12087 icon.toggleClass('fa-circle-chevron-down fa-circle-chevron-up');12036 icon.toggleClass('fa-circle-chevron-down fa-circle-chevron-up');
12037 drawer.trigger('inline-drawer-toggle');
12088 drawerContent.stop().slideToggle({12038 drawerContent.stop().slideToggle({
12089 complete: () => {12039 complete: () => {
12090 $(this).css('height', '');12040 $(this).css('height', '');
@@ -12181,7 +12131,7 @@ jQuery(async function () {
12181 if (!(e.target instanceof HTMLElement)) return;12131 if (!(e.target instanceof HTMLElement)) return;
12182 if (e.target.matches('#OpenAllWIEntries')) {12132 if (e.target.matches('#OpenAllWIEntries')) {
12183 document.querySelectorAll('#world_popup_entries_list .inline-drawer').forEach((/** @type {HTMLElement} */ drawer) => {12133 document.querySelectorAll('#world_popup_entries_list .inline-drawer').forEach((/** @type {HTMLElement} */ drawer) => {
12184 toggleDrawer(drawer, true);12134 delay(0).then(() => toggleDrawer(drawer, true));
12185 });12135 });
12186 } else if (e.target.matches('#CloseAllWIEntries')) {12136 } else if (e.target.matches('#CloseAllWIEntries')) {
12187 document.querySelectorAll('#world_popup_entries_list .inline-drawer').forEach((/** @type {HTMLElement} */ drawer) => {12137 document.querySelectorAll('#world_popup_entries_list .inline-drawer').forEach((/** @type {HTMLElement} */ drawer) => {
public/scripts/RossAscends-mods.js+137 -185
@@ -1,4 +1,4 @@
1import { DOMPurify, Bowser, slideToggle } from '../lib.js';1import { DOMPurify, Bowser } from '../lib.js';
22
3import {3import {
4 characters,4 characters,
@@ -19,7 +19,7 @@ import {
19 menu_type,19 menu_type,
20 substituteParams,20 substituteParams,
21 sendTextareaMessage,21 sendTextareaMessage,
22 getSlideToggleOptions,22 doNavbarIconClick,
23} from '../script.js';23} from '../script.js';
2424
25import {25import {
@@ -473,54 +473,72 @@ const saveUserInputDebounced = debounce(saveUserInput);
473473
474// Make the DIV element draggable:474// Make the DIV element draggable:
475475
476export function dragElement(elmnt) {476/**
477 var isHeaderBeingDragged = false;477 * Make the given element draggable. This is used for Moving UI.
478 var isMouseDown = false;478 * @param {JQuery} $elmnt - The element to make draggable.
479 */
480export function dragElement($elmnt) {
481 let actionType = null; // "drag" or "resize"
482 let isMouseDown = false;
479483
480 var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;484 let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
481 var height, width, top, left, right, bottom,485 let height, width, top, left, right, bottom,
482 maxX, maxY, winHeight, winWidth,486 maxX, maxY, winHeight, winWidth,
483 topbar, topBarFirstX, topBarLastY;487 topbar, topBarFirstX, topBarLastY;
484488
485 var elmntName = elmnt.attr('id');489 const elmntName = $elmnt.attr('id');
486 console.debug(`dragElement called for ${elmntName}`);
487 const elmntNameEscaped = $.escapeSelector(elmntName);490 const elmntNameEscaped = $.escapeSelector(elmntName);
488 const elmntHeader = $(`#${elmntNameEscaped}header`);491 const $elmntHeader = $(`#${elmntNameEscaped}header`);
489492
490 if (elmntHeader.length) {493 // Helper: Save position/size to state and emit events
491 elmntHeader.off('mousedown').on('mousedown', (e) => { //listener for drag handle repositioning494 function savePositionAndSize() {
492 isHeaderBeingDragged = true;495 if (!power_user.movingUIState[elmntName]) power_user.movingUIState[elmntName] = {};
493 isMouseDown = true;496 power_user.movingUIState[elmntName].top = top;
494 observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });497 power_user.movingUIState[elmntName].left = left;
495 dragMouseDown(e);498 power_user.movingUIState[elmntName].right = right;
496 });499 power_user.movingUIState[elmntName].bottom = bottom;
497 $(elmnt).off('mousedown').on('mousedown', () => { //listener for resize500 power_user.movingUIState[elmntName].margin = 'unset';
498 isMouseDown = true;501 if (actionType === 'resize') {
499 observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] });502 power_user.movingUIState[elmntName].width = width;
500 });503 power_user.movingUIState[elmntName].height = height;
504 eventSource.emit('resizeUI', elmntName);
505 }
506 saveSettingsDebounced();
501 }507 }
502508
509 // Helper: Clamp element within viewport
510 function clampToViewport() {
511 if (top <= 0) $elmnt.css('top', '0px');
512 else if (maxY >= winHeight) $elmnt.css('top', winHeight - maxY + top - 1 + 'px');
513 if (left <= 0) $elmnt.css('left', '0px');
514 else if (maxX >= winWidth) $elmnt.css('left', winWidth - maxX + left - 1 + 'px');
515 }
516
517 // Observer for style changes (position/size)
503 const observer = new MutationObserver((mutations) => {518 const observer = new MutationObserver((mutations) => {
504 const target = mutations[0].target;519 const $target = $(mutations[0].target);
505 if (!$(target).is(':visible') //abort if element is invisible520 if (
506 || $(target).hasClass('resizing') //being auto-resized by other JS code521 !$target.is(':visible') ||
507 || Number((String(target.height).replace('px', ''))) < 50 //too short522 $target.hasClass('resizing') ||
508 || Number((String(target.width).replace('px', ''))) < 50 //too narrow523 $target.height() < 50 ||
509 || power_user.movingUI === false // if MUI is not turned on524 $target.width() < 50 ||
510 || isMobile() // if it's a mobile screen525 power_user.movingUI === false ||
526 isMobile() ||
527 !isMouseDown
511 ) {528 ) {
529 observer.disconnect();
512 return;530 return;
513 }531 }
514532
515 const style = getComputedStyle(target);533 const style = getComputedStyle($target[0]);
516 height = parseInt(style.height);534 height = parseInt(style.height);
517 width = parseInt(style.width);535 width = parseInt(style.width);
518 top = parseInt(style.top);536 top = parseInt(style.top);
519 left = parseInt(style.left);537 left = parseInt(style.left);
520 right = parseInt(style.right);538 right = parseInt(style.right);
521 bottom = parseInt(style.bottom);539 bottom = parseInt(style.bottom);
522 maxX = parseInt(width + left);540 maxX = width + left;
523 maxY = parseInt(height + top);541 maxY = height + top;
524 winWidth = window.innerWidth;542 winWidth = window.innerWidth;
525 winHeight = window.innerHeight;543 winHeight = window.innerHeight;
526544
@@ -529,182 +547,119 @@ export function dragElement(elmnt) {
529 topBarFirstX = parseInt(topbarstyle.marginInline);547 topBarFirstX = parseInt(topbarstyle.marginInline);
530 topBarLastY = parseInt(topbarstyle.height);548 topBarLastY = parseInt(topbarstyle.height);
531549
532 //prepare an empty poweruser object for the item being altered if we don't have one already550 // Prepare state object if missing
533 if (!power_user.movingUIState[elmntName]) {551 if (!power_user.movingUIState[elmntName]) power_user.movingUIState[elmntName] = {};
534 console.debug(`adding config property for ${elmntName}`);
535 power_user.movingUIState[elmntName] = {};
536 }
537552
538 //handle resizing553 if (actionType === 'resize') {
539 if (!isHeaderBeingDragged && isMouseDown) { //if user is dragging the resize handle (not in header)
540 let imgHeight, imgWidth, imageAspectRatio;
541 let containerAspectRatio = height / width;554 let containerAspectRatio = height / width;
542555 if ($elmnt.attr('id').startsWith('zoomFor_')) {
543 //force aspect ratio for zoomed avatars556 const zoomedAvatarImage = $elmnt.find('.zoomed_avatar_img');
544 if ($(elmnt).attr('id').startsWith('zoomFor_')) {557 const imgHeight = zoomedAvatarImage.height();
545 let zoomedAvatarImage = $(elmnt).find('.zoomed_avatar_img');558 const imgWidth = zoomedAvatarImage.width();
546 imgHeight = zoomedAvatarImage.height();559 const imageAspectRatio = imgHeight / imgWidth;
547 imgWidth = zoomedAvatarImage.width();
548 imageAspectRatio = imgHeight / imgWidth;
549
550 // Maintain aspect ratio
551 if (containerAspectRatio !== imageAspectRatio) {560 if (containerAspectRatio !== imageAspectRatio) {
552 elmnt.css('width', elmnt.width());561 $elmnt.css('width', $elmnt.width());
553 elmnt.css('height', elmnt.width() * imageAspectRatio);562 $elmnt.css('height', $elmnt.width() * imageAspectRatio);
554 }563 }
555564 if (top + $elmnt.height() >= winHeight) {
556 // Prevent resizing offscreen565 $elmnt.css('height', winHeight - top - 1 + 'px');
557 if (top + elmnt.height() >= winHeight) {566 $elmnt.css('width', (winHeight - top - 1) / imageAspectRatio + 'px');
558 elmnt.css('height', winHeight - top - 1 + 'px');
559 elmnt.css('width', (winHeight - top - 1) / imageAspectRatio + 'px');
560 }567 }
561568 if (left + $elmnt.width() >= winWidth) {
562 if (left + elmnt.width() >= winWidth) {569 $elmnt.css('width', winWidth - left - 1 + 'px');
563 elmnt.css('width', winWidth - left - 1 + 'px');570 $elmnt.css('height', (winWidth - left - 1) * imageAspectRatio + 'px');
564 elmnt.css('height', (winWidth - left - 1) * imageAspectRatio + 'px');
565 }
566 } else { //prevent divs that are not zoomedAvatars from resizing offscreen
567
568 if (top + elmnt.height() >= winHeight) {
569 elmnt.css('height', winHeight - top - 1 + 'px');
570 }
571
572 if (left + elmnt.width() >= winWidth) {
573 elmnt.css('width', winWidth - left - 1 + 'px');
574 }571 }
572 } else {
573 if (top + $elmnt.height() >= winHeight) $elmnt.css('height', winHeight - top - 1 + 'px');
574 if (left + $elmnt.width() >= winWidth) $elmnt.css('width', winWidth - left - 1 + 'px');
575 }575 }
576
577 //prevent resizing from top left into the top bar
578 if (top < topBarLastY && maxX >= topBarFirstX && left <= topBarFirstX) {576 if (top < topBarLastY && maxX >= topBarFirstX && left <= topBarFirstX) {
579 elmnt.css('width', width - 1 + 'px');577 $elmnt.css('width', width - 1 + 'px');
580 }578 }
581579 $elmnt.css({ left, top });
582 //set css to prevent weird resize behavior (does not save)580 $elmnt.off('mouseup').on('mouseup', () => {
583 elmnt.css('left', left);581 if (
584 elmnt.css('top', top);582 power_user.movingUIState[elmntName].width === $elmnt.width() &&
585583 power_user.movingUIState[elmntName].height === $elmnt.height()
586 //set a listener for mouseup to save new width/height584 ) return;
587 $(window).off('mouseup').on('mouseup', () => {585 savePositionAndSize();
588 console.log(`Saving ${elmntName} Height/Width`);586 observer.disconnect();
589 // check if the height or width actually changed
590 if (power_user.movingUIState[elmntName].width === elmnt.width() && power_user.movingUIState[elmntName].height === elmnt.height()) {
591 console.log('no change detected, aborting save');
592 return;
593 }
594
595 power_user.movingUIState[elmntName].width = width;
596 power_user.movingUIState[elmntName].height = height;
597 eventSource.emit('resizeUI', elmntName);
598 saveSettingsDebounced();
599 imgHeight = null;
600 imgWidth = null;
601 height = null;
602 width = null;
603
604 containerAspectRatio = null;
605 imageAspectRatio = null;
606 $(window).off('mouseup');
607 });587 });
588 } else if (actionType === 'drag') {
589 clampToViewport();
608 }590 }
609591
610 //only record position changes if header is being dragged592 // Always update position in state
611 power_user.movingUIState[elmntName].top = top;593 savePositionAndSize();
612 power_user.movingUIState[elmntName].left = left;
613 power_user.movingUIState[elmntName].right = right;
614 power_user.movingUIState[elmntName].bottom = bottom;
615 power_user.movingUIState[elmntName].margin = 'unset';
616
617 //handle dragging hit detection to prevent dragging offscreen
618 if (isHeaderBeingDragged && isMouseDown) {
619
620 if (top <= 0) {
621 elmnt.css('top', '0px');
622 } else if (maxY >= winHeight) {
623 elmnt.css('top', winHeight - maxY + top - 1 + 'px');
624 }
625
626 if (left <= 0) {
627 elmnt.css('left', '0px');
628 } else if (maxX >= winWidth) {
629 elmnt.css('left', winWidth - maxX + left - 1 + 'px');
630 }
631 }
632
633 // Check if the element header exists and set the reposition listener on the grabber in the header
634 if (elmntHeader.length) {
635 elmntHeader.off('mousedown').on('mousedown', (e) => {
636 dragMouseDown(e);
637 });
638 } else { //if no header, put the listener on the elmnt itself.
639 elmnt.off('mousedown').on('mousedown', dragMouseDown);
640 }
641 });594 });
642595
596 // Mouse event handlers
643 function dragMouseDown(e) {597 function dragMouseDown(e) {
644
645 if (e) {598 if (e) {
646 isHeaderBeingDragged = true;599 actionType = 'drag';
600 isMouseDown = true;
647 e.preventDefault();601 e.preventDefault();
648 pos3 = e.clientX; //mouse X at click602 pos3 = e.clientX;
649 pos4 = e.clientY; //mouse Y at click603 pos4 = e.clientY;
650 }604 }
651 $(document).on('mouseup', closeDragElement);605 $(document).on('mouseup', closeDragElement);
652 $(document).on('mousemove', elementDrag);606 $(document).on('mousemove', elementDrag);
653 }607 }
654608
655 function elementDrag(e) {609 function elementDrag(e) {
656 if (!power_user.movingUIState[elmntName]) {610 if (!power_user.movingUIState[elmntName]) power_user.movingUIState[elmntName] = {};
657 power_user.movingUIState[elmntName] = {};
658 }
659
660 e = e || window.event;
661 e.preventDefault();611 e.preventDefault();
662612 pos1 = pos3 - e.clientX;
663 pos1 = pos3 - e.clientX; //X change amt (-1 or 1)613 pos2 = pos4 - e.clientY;
664 pos2 = pos4 - e.clientY; //Y change amt (-1 or 1)614 pos3 = e.clientX;
665 pos3 = e.clientX; //new mouse X615 pos4 = e.clientY;
666 pos4 = e.clientY; //new mouse Y616 $elmnt.attr('data-dragged', 'true');
667617 $elmnt.css('left', ($elmnt.offset().left - pos1) + 'px');
668 elmnt.attr('data-dragged', 'true');618 $elmnt.css('top', ($elmnt.offset().top - pos2) + 'px');
669619 $elmnt.css('margin', 'unset');
670 //first set css to computed values to avoid CSS NaN results from 'auto', etc620 $elmnt.css('height', height);
671 elmnt.css('left', (elmnt.offset().left) + 'px');621 $elmnt.css('width', width);
672 elmnt.css('top', (elmnt.offset().top) + 'px');
673
674 //then update element position styles to account for drag changes
675 elmnt.css('margin', 'unset');
676 elmnt.css('left', (elmnt.offset().left - pos1) + 'px');
677 elmnt.css('top', (elmnt.offset().top - pos2) + 'px');
678 /* elmnt.css('right', ((winWidth - maxX) + 'px'));
679 elmnt.css('bottom', ((winHeight - maxY) + 'px')); */
680
681 // Height/Width here are for visuals only, and are not saved to settings.
682 // This is required because some divs do hot have a set width/height
683 // and will default to shrink to min value of 100px set in CSS file
684 elmnt.css('height', height);
685 elmnt.css('width', width);
686 return;
687 }622 }
688623
689 function closeDragElement() {624 function closeDragElement() {
690 console.debug('drag finished');
691 isHeaderBeingDragged = false;
692 isMouseDown = false;625 isMouseDown = false;
626 actionType = null;
693 $(document).off('mouseup', closeDragElement);627 $(document).off('mouseup', closeDragElement);
694 $(document).off('mousemove', elementDrag);628 $(document).off('mousemove', elementDrag);
695 $('body').css('overflow', '');629 $elmnt.attr('data-dragged', 'false');
696 // Clear the "data-dragged" attribute
697 elmnt.attr('data-dragged', 'false');
698 observer.disconnect();630 observer.disconnect();
699 console.debug(`Saving ${elmntName} UI position`);631 savePositionAndSize();
700 saveSettingsDebounced();632 }
701 top = null;633
702 left = null;634 // Setup event listeners
703 right = null;635 if ($elmntHeader.length) {
704 bottom = null;636 $elmntHeader.off('mousedown').on('mousedown', (e) => {
705 maxX = null;637 if ($(e.target).hasClass('drag-grabber')) {
706 maxY = null;638 actionType = 'drag';
639 isMouseDown = true;
640 observer.observe($elmnt[0], { attributes: true, attributeFilter: ['style'] });
641 dragMouseDown(e);
642 }
643 });
707 }644 }
645
646 $elmnt.off('mousedown').on('mousedown', (e) => {
647 const rect = $elmnt[0].getBoundingClientRect();
648 const resizeMargin = 16;
649 const isNearRight = e.clientX > rect.right - resizeMargin;
650 const isNearBottom = e.clientY > rect.bottom - resizeMargin;
651 if (isNearRight && isNearBottom) {
652 actionType = 'resize';
653 isMouseDown = true;
654 observer.observe($elmnt[0], { attributes: true, attributeFilter: ['style'] });
655 }
656 });
657
658 $elmnt.off('mouseup').on('mouseup', () => {
659 isMouseDown = false;
660 actionType = null;
661 observer.disconnect();
662 });
708}663}
709664
710export async function initMovingUI() {665export async function initMovingUI() {
@@ -775,9 +730,8 @@ export function initRossMods() {
775 $(RightNavDrawerIcon).removeClass('drawerPinnedOpen');730 $(RightNavDrawerIcon).removeClass('drawerPinnedOpen');
776731
777 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {732 if ($(RightNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
778 slideToggle(RightNavPanel, getSlideToggleOptions());733 const toggle = $('#unimportantYes');
779 $(RightNavDrawerIcon).toggleClass('closedIcon openIcon');734 doNavbarIconClick.call(toggle);
780 $(RightNavPanel).toggleClass('openDrawer closedDrawer');
781 }735 }
782 }736 }
783 });737 });
@@ -793,14 +747,13 @@ export function initRossMods() {
793 $(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');747 $(LeftNavDrawerIcon).removeClass('drawerPinnedOpen');
794748
795 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {749 if ($(LeftNavPanel).hasClass('openDrawer') && $('.openDrawer').length > 1) {
796 slideToggle(LeftNavPanel, getSlideToggleOptions());750 const toggle = $('#ai-config-button>.drawer-toggle');
797 $(LeftNavDrawerIcon).toggleClass('closedIcon openIcon');751 doNavbarIconClick.call(toggle);
798 $(LeftNavPanel).toggleClass('openDrawer closedDrawer');
799 }752 }
800 }753 }
801 });754 });
802755
803 $(WIPanelPin).on('click', function () {756 $(WIPanelPin).on('click', async function () {
804 accountStorage.setItem('WINavLockOn', $(WIPanelPin).prop('checked'));757 accountStorage.setItem('WINavLockOn', $(WIPanelPin).prop('checked'));
805 if ($(WIPanelPin).prop('checked') == true) {758 if ($(WIPanelPin).prop('checked') == true) {
806 console.debug('adding pin class to WI');759 console.debug('adding pin class to WI');
@@ -813,9 +766,8 @@ export function initRossMods() {
813766
814 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {767 if ($(WorldInfo).hasClass('openDrawer') && $('.openDrawer').length > 1) {
815 console.debug('closing WI after lock removal');768 console.debug('closing WI after lock removal');
816 slideToggle(WorldInfo, getSlideToggleOptions());769 const toggle = $('#WI-SP-button>.drawer-toggle');
817 $(WIDrawerIcon).toggleClass('closedIcon openIcon');770 doNavbarIconClick.call(toggle);
818 $(WorldInfo).toggleClass('openDrawer closedDrawer');
819 }771 }
820 }772 }
821 });773 });
public/scripts/authors-note.js+17 -14
@@ -1,9 +1,11 @@
1import {1import {
2 MAX_INJECTION_DEPTH,
2 animation_duration,3 animation_duration,
3 chat_metadata,4 chat_metadata,
4 eventSource,5 eventSource,
5 event_types,6 event_types,
6 extension_prompt_roles,7 extension_prompt_roles,
8 extension_prompt_types,
7 saveSettingsDebounced,9 saveSettingsDebounced,
8 this_chid,10 this_chid,
9} from '../script.js';11} from '../script.js';
@@ -347,7 +349,7 @@ export function setFloatingPrompt() {
347 }349 }
348350
349 if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {351 if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) {
350 context.setExtensionPrompt(MODULE_NAME, '');352 context.setExtensionPrompt(MODULE_NAME, '', extension_prompt_types.NONE, MAX_INJECTION_DEPTH);
351 $('#extension_floating_counter').text('(disabled)');353 $('#extension_floating_counter').text('(disabled)');
352 shouldWIAddPrompt = false;354 shouldWIAddPrompt = false;
353 return;355 return;
@@ -380,7 +382,7 @@ export function setFloatingPrompt() {
380 }382 }
381 context.setExtensionPrompt(383 context.setExtensionPrompt(
382 MODULE_NAME,384 MODULE_NAME,
383 prompt,385 String(prompt),
384 chat_metadata[metadata_keys.position],386 chat_metadata[metadata_keys.position],
385 chat_metadata[metadata_keys.depth],387 chat_metadata[metadata_keys.depth],
386 extension_settings.note.allowWIScan,388 extension_settings.note.allowWIScan,
@@ -396,37 +398,38 @@ function onANMenuItemClick() {
396 }398 }
397399
398 //show AN if it's hidden400 //show AN if it's hidden
399 if ($('#floatingPrompt').css('display') !== 'flex') {401 const $ANcontainer = $('#floatingPrompt');
400 $('#floatingPrompt').addClass('resizing');402 if ($ANcontainer.css('display') !== 'flex') {
401 $('#floatingPrompt').css('display', 'flex');403 $ANcontainer.addClass('resizing');
402 $('#floatingPrompt').css('opacity', 0.0);404 $ANcontainer.css('display', 'flex');
403 $('#floatingPrompt').transition({405 $ANcontainer.css('opacity', 0.0);
406 $ANcontainer.transition({
404 opacity: 1.0,407 opacity: 1.0,
405 duration: animation_duration,408 duration: animation_duration,
406 }, async function () {409 }, async function () {
407 await delay(50);410 await delay(50);
408 $('#floatingPrompt').removeClass('resizing');411 $ANcontainer.removeClass('resizing');
409 });412 });
410413
411 //auto-open the main AN inline drawer414 //auto-open the main AN inline drawer
412 if ($('#ANBlockToggle')415 if ($('#ANBlockToggle')
413 .siblings('.inline-drawer-content')416 .siblings('.inline-drawer-content')
414 .css('display') !== 'block') {417 .css('display') !== 'block') {
415 $('#floatingPrompt').addClass('resizing');418 $ANcontainer.addClass('resizing');
416 $('#ANBlockToggle').click();419 $('#ANBlockToggle').trigger('click');
417 }420 }
418 } else {421 } else {
419 //hide AN if it's already displayed422 //hide AN if it's already displayed
420 $('#floatingPrompt').addClass('resizing');423 $ANcontainer.addClass('resizing');
421 $('#floatingPrompt').transition({424 $ANcontainer.transition({
422 opacity: 0.0,425 opacity: 0.0,
423 duration: animation_duration,426 duration: animation_duration,
424 }, async function () {427 }, async function () {
425 await delay(50);428 await delay(50);
426 $('#floatingPrompt').removeClass('resizing');429 $ANcontainer.removeClass('resizing');
427 });430 });
428 setTimeout(function () {431 setTimeout(function () {
429 $('#floatingPrompt').hide();432 $ANcontainer.hide();
430 }, animation_duration);433 }, animation_duration);
431 }434 }
432435
public/scripts/extensions/assets/style.css+1 -1
@@ -65,7 +65,7 @@
65.asset-download-button-text {65.asset-download-button-text {
66 font: bold 20px "Quicksand", san-serif;66 font: bold 20px "Quicksand", san-serif;
67 color: #ffffff;67 color: #ffffff;
68 transition: all 0.2s;68 transition: all var(--animation-duration-2x);
69}69}
7070
71.asset-download-button-loading .asset-download-button-text {71.asset-download-button-loading .asset-download-button-text {
public/scripts/extensions/gallery/index.js+17 -2
@@ -4,6 +4,8 @@ import {
4 characters,4 characters,
5 getRequestHeaders,5 getRequestHeaders,
6 event_types,6 event_types,
7 animation_duration,
8 animation_easing,
7} from '../../../script.js';9} from '../../../script.js';
8import { groups, selected_group } from '../../group-chats.js';10import { groups, selected_group } from '../../group-chats.js';
9import { loadFileToDocument, delay, getBase64Async, getSanitizedFilename } from '../../utils.js';11import { loadFileToDocument, delay, getBase64Async, getSanitizedFilename } from '../../utils.js';
@@ -32,7 +34,15 @@ let galleryMaxRows = 3;
32$('#movingDivs').on('click', '.dragClose', function () {34$('#movingDivs').on('click', '.dragClose', function () {
33 const relatedId = $(this).data('related-id');35 const relatedId = $(this).data('related-id');
34 if (!relatedId) return;36 if (!relatedId) return;
35 $(`#movingDivs > .draggable[id="${relatedId}"]`).remove();37 const relatedElement = $(`#movingDivs > .draggable[id="${relatedId}"]`);
38 relatedElement.transition({
39 opacity: 0,
40 duration: animation_duration,
41 easing: animation_easing,
42 complete: () => {
43 relatedElement.remove();
44 },
45 });
36});46});
3747
38const CUSTOM_GALLERY_REMOVED_EVENT = 'galleryRemoved';48const CUSTOM_GALLERY_REMOVED_EVENT = 'galleryRemoved';
@@ -373,7 +383,7 @@ async function makeMovable(url) {
373 const id = 'gallery';383 const id = 'gallery';
374 const template = $('#generic_draggable_template').html();384 const template = $('#generic_draggable_template').html();
375 const newElement = $(template);385 const newElement = $(template);
376 newElement.css('background-color', 'var(--SmartThemeBlurTintColor)');386 newElement.css({ 'background-color': 'var(--SmartThemeBlurTintColor)', 'opacity': 0 });
377 newElement.attr('forChar', id);387 newElement.attr('forChar', id);
378 newElement.attr('id', id);388 newElement.attr('id', id);
379 newElement.find('.drag-grabber').attr('id', `${id}header`);389 newElement.find('.drag-grabber').attr('id', `${id}header`);
@@ -505,6 +515,11 @@ async function makeMovable(url) {
505 loadMovingUIState();515 loadMovingUIState();
506 $(`.draggable[forChar="${id}"]`).css('display', 'block');516 $(`.draggable[forChar="${id}"]`).css('display', 'block');
507 dragElement(newElement);517 dragElement(newElement);
518 newElement.transition({
519 opacity: 1,
520 duration: animation_duration,
521 easing: animation_easing,
522 });
508523
509 $(`.draggable[forChar="${id}"] img`).on('dragstart', (e) => {524 $(`.draggable[forChar="${id}"] img`).on('dragstart', (e) => {
510 console.log('saw drag on avatar!');525 console.log('saw drag on avatar!');
public/scripts/extensions/memory/index.js+3 -1
@@ -16,6 +16,7 @@ import {
16 getMaxContextSize,16 getMaxContextSize,
17 setExtensionPrompt,17 setExtensionPrompt,
18 streamingProcessor,18 streamingProcessor,
19 animation_easing,
19} from '../../../script.js';20} from '../../../script.js';
20import { is_group_generating, selected_group } from '../../group-chats.js';21import { is_group_generating, selected_group } from '../../group-chats.js';
21import { loadMovingUIState } from '../../power-user.js';22import { loadMovingUIState } from '../../power-user.js';
@@ -972,6 +973,7 @@ function doPopout(e) {
972 </div>`;973 </div>`;
973 const newElement = $(template);974 const newElement = $(template);
974 newElement.attr('id', 'summaryExtensionPopout')975 newElement.attr('id', 'summaryExtensionPopout')
976 .css('opacity', 0)
975 .removeClass('zoomed_avatar')977 .removeClass('zoomed_avatar')
976 .addClass('draggable')978 .addClass('draggable')
977 .empty();979 .empty();
@@ -980,13 +982,13 @@ function doPopout(e) {
980 originalElement.html('<div class="flex-container alignitemscenter justifyCenter wide100p"><small>Currently popped out</small></div>');982 originalElement.html('<div class="flex-container alignitemscenter justifyCenter wide100p"><small>Currently popped out</small></div>');
981 newElement.append(controlBarHtml).append(originalHTMLClone);983 newElement.append(controlBarHtml).append(originalHTMLClone);
982 $('body').append(newElement);984 $('body').append(newElement);
985 newElement.transition({ opacity: 1, duration: animation_duration, easing: animation_easing });
983 $('#summaryExtensionDrawerContents').addClass('scrollableInnerFull');986 $('#summaryExtensionDrawerContents').addClass('scrollableInnerFull');
984 setMemoryContext(prevSummaryBoxContents, false); //paste prev summary box contents into popout box987 setMemoryContext(prevSummaryBoxContents, false); //paste prev summary box contents into popout box
985 setupListeners();988 setupListeners();
986 loadSettings();989 loadSettings();
987 loadMovingUIState();990 loadMovingUIState();
988991
989 $('#summaryExtensionPopout').fadeIn(animation_duration);
990 dragElement(newElement);992 dragElement(newElement);
991993
992 //setup listener for close button to restore extensions menu994 //setup listener for close button to restore extensions menu
public/scripts/extensions/quick-reply/style.css+13 -16
@@ -18,7 +18,7 @@
18#qr--bar {18#qr--bar {
19 outline: none;19 outline: none;
20 margin: 0;20 margin: 0;
21 transition: 0.3s;21 transition: var(--animation-duration-2x);
22 opacity: 0.7;22 opacity: 0.7;
23 display: flex;23 display: flex;
24 align-items: center;24 align-items: center;
@@ -34,14 +34,12 @@
34 right: 0.25em;34 right: 0.25em;
35 top: 0;35 top: 0;
36}36}
37
38/*hide QR popout for mobile*/37/*hide QR popout for mobile*/
39@media screen and (max-width: 1000px) {38@media screen and (max-width: 1000px) {
40 #qr--bar > #qr--popoutTrigger {39 #qr--bar > #qr--popoutTrigger {
41 display:none;40 display: none;
42 }41 }
43}42}
44
45#qr--bar.popoutVisible {43#qr--bar.popoutVisible {
46 padding-right: 2.5em;44 padding-right: 2.5em;
47}45}
@@ -61,9 +59,8 @@
61 aspect-ratio: 1 / 1;59 aspect-ratio: 1 / 1;
62 font-size: 20px;60 font-size: 20px;
63 opacity: 0.5;61 opacity: 0.5;
64 transition: all 250ms;62 transition: all var(--animation-duration-2x);
65}63}
66
67#qr--popout > .qr--body {64#qr--popout > .qr--body {
68 overflow-y: auto;65 overflow-y: auto;
69}66}
@@ -124,7 +121,7 @@
124 padding: 3px 5px;121 padding: 3px 5px;
125 margin: 3px 0;122 margin: 3px 0;
126 cursor: pointer;123 cursor: pointer;
127 transition: 0.3s;124 transition: var(--animation-duration-2x);
128 display: flex;125 display: flex;
129 align-items: center;126 align-items: center;
130 justify-content: center;127 justify-content: center;
@@ -175,7 +172,7 @@
175 overflow: visible;172 overflow: visible;
176}173}
177.ctx-menu .ctx-item .qr--hidden {174.ctx-menu .ctx-item .qr--hidden {
178 display: none;175 display: none;
179}176}
180.list-group .list-group-item.ctx-header {177.list-group .list-group-item.ctx-header {
181 font-weight: bold;178 font-weight: bold;
@@ -248,7 +245,7 @@
248 display: flex;245 display: flex;
249 align-items: center;246 align-items: center;
250 opacity: 0;247 opacity: 0;
251 transition: 100ms;248 transition: var(--animation-duration);
252 margin: -2px 0 -11px 0;249 margin: -2px 0 -11px 0;
253 position: relative;250 position: relative;
254}251}
@@ -461,7 +458,7 @@
461 cursor: not-allowed;458 cursor: not-allowed;
462 opacity: 0.5;459 opacity: 0.5;
463 pointer-events: none;460 pointer-events: none;
464 transition: 200ms;461 transition: var(--animation-duration-2x);
465 border-color: transparent;462 border-color: transparent;
466}463}
467.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting.qr--isPaused #qr--modal-debugButtons .menu_button:not(#qr--modal-minimize, #qr--modal-maximize) {464.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting.qr--isPaused #qr--modal-debugButtons .menu_button:not(#qr--modal-minimize, #qr--modal-maximize) {
@@ -492,7 +489,7 @@
492 width: 6px;489 width: 6px;
493 background-color: var(--SmartThemeBorderColor);490 background-color: var(--SmartThemeBorderColor);
494 border: 2px solid var(--SmartThemeBlurTintColor);491 border: 2px solid var(--SmartThemeBlurTintColor);
495 transition: border-color 200ms, background-color 200ms;492 transition: border-color var(--animation-duration-2x), background-color var(--animation-duration-2x);
496 cursor: w-resize;493 cursor: w-resize;
497}494}
498.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--resizeHandle:hover {495.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor.qr--isExecuting #qr--resizeHandle:hover {
@@ -574,7 +571,7 @@
574 gap: 1em;571 gap: 1em;
575 text-align: left;572 text-align: left;
576 opacity: 0.75;573 opacity: 0.75;
577 transition: 200ms;574 transition: var(--animation-duration-2x);
578 cursor: pointer;575 cursor: pointer;
579}576}
580.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor > #qr--main > .qr--labels > label .qr--modal-switcherList .qr--modal-switcherItem:hover,577.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor > #qr--main > .qr--labels > label .qr--modal-switcherList .qr--modal-switcherItem:hover,
@@ -736,7 +733,7 @@
736 display: flex;733 display: flex;
737}734}
738.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-executeButtons #qr--modal-execute {735.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-executeButtons #qr--modal-execute {
739 transition: 200ms;736 transition: var(--animation-duration-2x);
740 filter: grayscale(0);737 filter: grayscale(0);
741}738}
742.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-executeButtons #qr--modal-execute.qr--busy {739.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-executeButtons #qr--modal-execute.qr--busy {
@@ -825,7 +822,7 @@
825 position: absolute;822 position: absolute;
826 inset: 0;823 inset: 0;
827 right: calc(100% - var(--prog) * 1%);824 right: calc(100% - var(--prog) * 1%);
828 transition: 200ms;825 transition: var(--animation-duration-2x);
829}826}
830.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-executeProgress.qr--paused:after {827.popup:has(#qr--modalEditor) .popup-content > #qr--modalEditor #qr--modal-executeProgress.qr--paused:after {
831 animation-name: qr--progressPulse;828 animation-name: qr--progressPulse;
@@ -1053,7 +1050,7 @@
1053 white-space: pre;1050 white-space: pre;
1054 font-weight: normal;1051 font-weight: normal;
1055 box-shadow: 0 0 0;1052 box-shadow: 0 0 0;
1056 transition: 200ms;1053 transition: var(--animation-duration-2x);
1057}1054}
1058.popup:has(.qr--transferModal) .popup-button-ok:after {1055.popup:has(.qr--transferModal) .popup-button-ok:after {
1059 content: 'Transfer';1056 content: 'Transfer';
@@ -1068,7 +1065,7 @@
1068 white-space: pre;1065 white-space: pre;
1069 font-weight: normal;1066 font-weight: normal;
1070 box-shadow: 0 0 0;1067 box-shadow: 0 0 0;
1071 transition: 200ms;1068 transition: var(--animation-duration-2x);
1072}1069}
1073.popup:has(.qr--transferModal) .qr--copy:after {1070.popup:has(.qr--transferModal) .qr--copy:after {
1074 content: 'Copy';1071 content: 'Copy';
public/scripts/extensions/quick-reply/style.less+534 -370
@@ -1,11 +1,16 @@
1@keyframes qr--success {1@keyframes qr--success {
2 0%, 100% {2
3 0%,
4 100% {
3 color: var(--SmartThemeBodyColor);5 color: var(--SmartThemeBodyColor);
4 }6 }
5 25%, 75% {7
8 25%,
9 75% {
6 color: rgb(81, 163, 81);10 color: rgb(81, 163, 81);
7 }11 }
8}12}
13
9&.qr--success {14&.qr--success {
10 animation-name: qr--success;15 animation-name: qr--success;
11 animation-duration: 3s;16 animation-duration: 3s;
@@ -13,10 +18,11 @@
13 animation-delay: 0s;18 animation-delay: 0s;
14 animation-iteration-count: 1;19 animation-iteration-count: 1;
15}20}
21
16#qr--bar {22#qr--bar {
17 outline: none;23 outline: none;
18 margin: 0;24 margin: 0;
19 transition: 0.3s;25 transition: var(--animation-duration-2x);
20 opacity: 0.7;26 opacity: 0.7;
21 display: flex;27 display: flex;
22 align-items: center;28 align-items: center;
@@ -33,6 +39,14 @@
33 top: 0;39 top: 0;
34 }40 }
35}41}
42
43/*hide QR popout for mobile*/
44@media screen and (max-width: 1000px) {
45 #qr--bar>#qr--popoutTrigger {
46 display: none;
47 }
48}
49
36#qr--bar.popoutVisible {50#qr--bar.popoutVisible {
37 padding-right: 2.5em;51 padding-right: 2.5em;
38}52}
@@ -54,7 +68,7 @@
54 aspect-ratio: 1 / 1;68 aspect-ratio: 1 / 1;
55 font-size: 20px;69 font-size: 20px;
56 opacity: 0.5;70 opacity: 0.5;
57 transition: all 250ms;71 transition: all var(--animation-duration-2x);
58 }72 }
59 }73 }
60 }74 }
@@ -68,17 +82,21 @@
68#qr--popout>.qr--body {82#qr--popout>.qr--body {
69 >.qr--buttons {83 >.qr--buttons {
70 --qr--color: transparent;84 --qr--color: transparent;
85
71 &.qr--color {86 &.qr--color {
72 background-color: var(--qr--color);87 background-color: var(--qr--color);
73 }88 }
89
74 &.qr--borderColor {90 &.qr--borderColor {
75 background-color: transparent;91 background-color: transparent;
76 border-left: 5px solid var(--qr--color);92 border-left: 5px solid var(--qr--color);
77 border-right: 5px solid var(--qr--color);93 border-right: 5px solid var(--qr--color);
78 }94 }
95
79 &:has(.qr--buttons.qr--color) {96 &:has(.qr--buttons.qr--color) {
80 margin: 5px;97 margin: 5px;
81 }98 }
99
82 margin: 0;100 margin: 0;
83 padding: 0;101 padding: 0;
84 display: flex;102 display: flex;
@@ -89,6 +107,7 @@
89107
90 >.qr--buttons {108 >.qr--buttons {
91 display: contents;109 display: contents;
110
92 &.qr--color {111 &.qr--color {
93 .qr--button:before {112 .qr--button:before {
94 content: '';113 content: '';
@@ -97,11 +116,14 @@
97 inset: -5px;116 inset: -5px;
98 z-index: -1;117 z-index: -1;
99 }118 }
119
100 &.qr--borderColor {120 &.qr--borderColor {
101 .qr--button:before {121 .qr--button:before {
102 display: none;122 display: none;
103 }123 }
104 &:before, &:after {124
125 &:before,
126 &:after {
105 content: '';127 content: '';
106 width: 5px;128 width: 5px;
107 background-color: var(--qr--color);129 background-color: var(--qr--color);
@@ -118,7 +140,7 @@
118 padding: 3px 5px;140 padding: 3px 5px;
119 margin: 3px 0;141 margin: 3px 0;
120 cursor: pointer;142 cursor: pointer;
121 transition: 0.3s;143 transition: var(--animation-duration-2x);
122 display: flex;144 display: flex;
123 align-items: center;145 align-items: center;
124 justify-content: center;146 justify-content: center;
@@ -132,6 +154,7 @@
132 .qr--hidden {154 .qr--hidden {
133 display: none;155 display: none;
134 }156 }
157
135 .qr--button-icon {158 .qr--button-icon {
136 margin: 0 0.5em;159 margin: 0 0.5em;
137 }160 }
@@ -274,18 +297,22 @@
274 display: flex;297 display: flex;
275 align-items: center;298 align-items: center;
276 opacity: 0;299 opacity: 0;
277 transition: 100ms;300 transition: var(--animation-duration);
278 margin: -2px 0 -11px 0;301 margin: -2px 0 -11px 0;
279 position: relative;302 position: relative;
303
280 .qr--actions {304 .qr--actions {
281 display: flex;305 display: flex;
282 gap: 0.25em;306 gap: 0.25em;
283 flex: 0 0 auto;307 flex: 0 0 auto;
308
284 .qr--action {309 .qr--action {
285 margin: 0;310 margin: 0;
286 }311 }
287 }312 }
288 &:before, &:after {313
314 &:before,
315 &:after {
289 content: "";316 content: "";
290 display: block;317 display: block;
291 flex: 1 1 auto;318 flex: 1 1 auto;
@@ -293,10 +320,13 @@
293 margin: 0 1em;320 margin: 0 1em;
294 height: 0;321 height: 0;
295 }322 }
296 &:hover, &:focus-within {323
324 &:hover,
325 &:focus-within {
297 opacity: 1;326 opacity: 1;
298 }327 }
299 }328 }
329
300 >.qr--set-item .qr--content {330 >.qr--set-item .qr--content {
301 display: flex;331 display: flex;
302 flex-direction: row;332 flex-direction: row;
@@ -336,9 +366,11 @@
336 display: flex;366 display: flex;
337 align-items: center;367 align-items: center;
338 gap: 0.5em;368 gap: 0.5em;
369
339 .qr--set-itemIcon:not(.fa-solid) {370 .qr--set-itemIcon:not(.fa-solid) {
340 display: none;371 display: none;
341 }372 }
373
342 .qr--set-itemLabel {374 .qr--set-itemLabel {
343 min-width: 24px;375 min-width: 24px;
344 }376 }
@@ -384,15 +416,16 @@
384 align-items: baseline;416 align-items: baseline;
385 }417 }
386 }418 }
387 >#qr--autoExec {
388 .checkbox_label {
389 text-wrap: nowrap;
390419
391 .fa-fw {420 >#qr--autoExec {
392 margin-right: 2px;421 .checkbox_label {
393 }422 text-wrap: nowrap;
423
424 .fa-fw {
425 margin-right: 2px;
394 }426 }
395 }427 }
428 }
396}429}
397430
398431
@@ -433,65 +466,75 @@
433 bottom: unset;466 bottom: unset;
434 margin: unset;467 margin: unset;
435 padding: 0;468 padding: 0;
469
436 &::backdrop {470 &::backdrop {
437 backdrop-filter: unset;471 backdrop-filter: unset;
438 background-color: transparent;472 background-color: transparent;
439 }473 }
474
440 .popup-body {475 .popup-body {
441 flex: 0 0 auto;476 flex: 0 0 auto;
442 height: min-content;477 height: min-content;
443 width: min-content;478 width: min-content;
444 }479 }
480
445 .popup-content {481 .popup-content {
446 flex: 0 0 auto;482 flex: 0 0 auto;
447 margin-top: 0;483 margin-top: 0;
448484
449 > #qr--modalEditor {485 >#qr--modalEditor {
450 max-height: 50vh;486 max-height: 50vh;
451 > #qr--main,487
452 > #qr--resizeHandle,488 >#qr--main,
453 > #qr--qrOptions > h3,489 >#qr--resizeHandle,
454 > #qr--qrOptions > #qr--modal-executeButtons,490 >#qr--qrOptions>h3,
455 > #qr--qrOptions > #qr--modal-executeProgress491 >#qr--qrOptions>#qr--modal-executeButtons,
456 {492 >#qr--qrOptions>#qr--modal-executeProgress {
457 display: none;493 display: none;
458 }494 }
495
459 #qr--qrOptions {496 #qr--qrOptions {
460 width: auto;497 width: auto;
461 }498 }
499
462 #qr--modal-debugButtons .qr--modal-debugButton#qr--modal-maximize {500 #qr--modal-debugButtons .qr--modal-debugButton#qr--modal-maximize {
463 display: flex;501 display: flex;
464 }502 }
503
465 #qr--modal-debugButtons .qr--modal-debugButton#qr--modal-minimize {504 #qr--modal-debugButtons .qr--modal-debugButton#qr--modal-minimize {
466 display: none;505 display: none;
467 }506 }
507
468 #qr--modal-debugState {508 #qr--modal-debugState {
469 padding-top: 0;509 padding-top: 0;
470 }510 }
471 }511 }
472 }512 }
473 }513 }
474 &:has(.qr--isExecuting) {514
475 .popup-controls {515 &:has(.qr--isExecuting) {
476 display: none;516 .popup-controls {
477 }517 display: none;
478518 }
479 .qr--highlight {519
480 position: absolute;520 .qr--highlight {
481 z-index: 50000;521 position: absolute;
482 pointer-events: none;522 z-index: 50000;
483 background-color: rgb(47 150 180 / 0.5);523 pointer-events: none;
484 &.qr--unresolved {524 background-color: rgb(47 150 180 / 0.5);
485 background-color: rgb(255 255 0 / 0.5);525
486 }526 &.qr--unresolved {
487 }527 background-color: rgb(255 255 0 / 0.5);
488 .qr--highlight-secondary {528 }
489 position: absolute;529 }
490 z-index: 50000;530
491 pointer-events: none;531 .qr--highlight-secondary {
492 border: 3px solid red;532 position: absolute;
493 }533 z-index: 50000;
494 }534 pointer-events: none;
535 border: 3px solid red;
536 }
537 }
495538
496 .popup-content {539 .popup-content {
497 display: flex;540 display: flex;
@@ -504,36 +547,41 @@
504 gap: 1em;547 gap: 1em;
505 overflow: hidden;548 overflow: hidden;
506549
507 &.qr--isExecuting {550 &.qr--isExecuting {
508 #qr--main > h3:first-child,551
509 #qr--main > .qr--labels,552 #qr--main>h3:first-child,
510 #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings,553 #qr--main>.qr--labels,
511 #qr--qrOptions > h3:first-child,554 #qr--main>.qr--modal-messageContainer>.qr--modal-editorSettings,
512 #qr--qrOptions > #qr--ctxEditor,555 #qr--qrOptions>h3:first-child,
513 #qr--qrOptions > .qr--ctxEditorActions,556 #qr--qrOptions>#qr--ctxEditor,
514 #qr--qrOptions > .qr--ctxEditorActions + h3,557 #qr--qrOptions>.qr--ctxEditorActions,
515 #qr--qrOptions > .qr--ctxEditorActions + h3 + div558 #qr--qrOptions>.qr--ctxEditorActions+h3,
516 {559 #qr--qrOptions>.qr--ctxEditorActions+h3+div {
517 display: none;560 display: none;
518 }561 }
519 #qr--main > .qr--modal-messageContainer > #qr--modal-messageHolder > #qr--modal-message {562
520 visibility: hidden;563 #qr--main>.qr--modal-messageContainer>#qr--modal-messageHolder>#qr--modal-message {
521 }564 visibility: hidden;
522 #qr--modal-debugButtons {565 }
523 display: flex;566
567 #qr--modal-debugButtons {
568 display: flex;
569
524 .menu_button:not(#qr--modal-minimize, #qr--modal-maximize) {570 .menu_button:not(#qr--modal-minimize, #qr--modal-maximize) {
525 cursor: not-allowed;571 cursor: not-allowed;
526 opacity: 0.5;572 opacity: 0.5;
527 pointer-events: none;573 pointer-events: none;
528 transition: 200ms;574 transition: var(--animation-duration-2x);
529 border-color: transparent;575 border-color: transparent;
530 }576 }
531 }577 }
578
532 &.qr--isPaused #qr--modal-debugButtons {579 &.qr--isPaused #qr--modal-debugButtons {
533 .menu_button:not(#qr--modal-minimize, #qr--modal-maximize) {580 .menu_button:not(#qr--modal-minimize, #qr--modal-maximize) {
534 cursor: pointer;581 cursor: pointer;
535 opacity: 1;582 opacity: 1;
536 pointer-events: all;583 pointer-events: all;
584
537 &#qr--modal-resume {585 &#qr--modal-resume {
538 animation-name: qr--debugPulse;586 animation-name: qr--debugPulse;
539 animation-duration: 1500ms;587 animation-duration: 1500ms;
@@ -541,73 +589,90 @@
541 animation-delay: 0s;589 animation-delay: 0s;
542 animation-iteration-count: infinite;590 animation-iteration-count: infinite;
543 }591 }
592
544 &#qr--modal-resume {593 &#qr--modal-resume {
545 border-color: rgb(81, 163, 81);594 border-color: rgb(81, 163, 81);
546 }595 }
596
547 &#qr--modal-step {597 &#qr--modal-step {
548 border-color: var(--SmartThemeQuoteColor);598 border-color: var(--SmartThemeQuoteColor);
549 }599 }
600
550 &#qr--modal-stepInto {601 &#qr--modal-stepInto {
551 border-color: var(--SmartThemeQuoteColor);602 border-color: var(--SmartThemeQuoteColor);
552 }603 }
604
553 &#qr--modal-stepOut {605 &#qr--modal-stepOut {
554 border-color: var(--SmartThemeQuoteColor);606 border-color: var(--SmartThemeQuoteColor);
555 }607 }
556 }608 }
557 }609 }
558 #qr--resizeHandle {610
559 width: 6px;611 #qr--resizeHandle {
560 background-color: var(--SmartThemeBorderColor);612 width: 6px;
561 border: 2px solid var(--SmartThemeBlurTintColor);613 background-color: var(--SmartThemeBorderColor);
562 transition: border-color 200ms, background-color 200ms;614 border: 2px solid var(--SmartThemeBlurTintColor);
563 cursor: w-resize;615 transition: border-color var(--animation-duration-2x), background-color var(--animation-duration-2x);
564 &:hover {616 cursor: w-resize;
565 background-color: var(--SmartThemeQuoteColor);617
566 border-color: var(--SmartThemeQuoteColor);618 &:hover {
567 }619 background-color: var(--SmartThemeQuoteColor);
568 }620 border-color: var(--SmartThemeQuoteColor);
569 #qr--qrOptions {621 }
570 width: var(--width, auto);622 }
571 }623
572 }624 #qr--qrOptions {
573625 width: var(--width, auto);
574 > #qr--main {626 }
575 flex: 1 1 auto;627 }
576 display: flex;628
577 flex-direction: column;629 >#qr--main {
578 overflow: hidden;630 flex: 1 1 auto;
579 > .qr--labels {631 display: flex;
580 flex: 0 0 auto;632 flex-direction: column;
581 display: flex;633 overflow: hidden;
582 flex-direction: row;634
583 gap: 0.5em;635 >.qr--labels {
584 padding: 1px;636 flex: 0 0 auto;
585 > label, > .label {637 display: flex;
586 flex: 1 1 1px;638 flex-direction: row;
587 display: flex;639 gap: 0.5em;
588 flex-direction: column;640 padding: 1px;
641
642 >label,
643 >.label {
644 flex: 1 1 1px;
645 display: flex;
646 flex-direction: column;
589 position: relative;647 position: relative;
648
590 &.qr--fit {649 &.qr--fit {
591 flex: 0 0 auto;650 flex: 0 0 auto;
592 justify-content: center;651 justify-content: center;
593 }652 }
653
594 .qr--inputGroup {654 .qr--inputGroup {
595 display: flex;655 display: flex;
596 align-items: baseline;656 align-items: baseline;
597 gap: 0.5em;657 gap: 0.5em;
658
598 input {659 input {
599 flex: 1 1 auto;660 flex: 1 1 auto;
600 }661 }
601 }662 }
602 .qr--labelText {663
603 flex: 1 1 auto;664 .qr--labelText {
604 }665 flex: 1 1 auto;
605 .qr--labelHint {666 }
606 flex: 1 1 auto;667
607 }668 .qr--labelHint {
608 input {669 flex: 1 1 auto;
609 flex: 0 0 auto;670 }
610 }671
672 input {
673 flex: 0 0 auto;
674 }
675
611 .qr--modal-switcherList {676 .qr--modal-switcherList {
612 background-color: var(--stcdx--bgColor);677 background-color: var(--stcdx--bgColor);
613 border: 1px solid var(--SmartThemeBorderColor);678 border: 1px solid var(--SmartThemeBorderColor);
@@ -625,25 +690,32 @@
625 list-style: none;690 list-style: none;
626 z-index: 40000;691 z-index: 40000;
627 max-width: 100%;692 max-width: 100%;
693
628 .qr--modal-switcherItem {694 .qr--modal-switcherItem {
629 display: flex;695 display: flex;
630 gap: 1em;696 gap: 1em;
631 text-align: left;697 text-align: left;
632 opacity: 0.75;698 opacity: 0.75;
633 transition: 200ms;699 transition: var(--animation-duration-2x);
634 cursor: pointer;700 cursor: pointer;
701
635 &:hover {702 &:hover {
636 opacity: 1;703 opacity: 1;
637 }704 }
705
638 &.qr--current {706 &.qr--current {
639 opacity: 1;707 opacity: 1;
640 .qr--label, .qr--id {708
709 .qr--label,
710 .qr--id {
641 font-weight: bold;711 font-weight: bold;
642 }712 }
643 }713 }
644 }714 }
715
645 .qr--label {716 .qr--label {
646 white-space: nowrap;717 white-space: nowrap;
718
647 .menu_button {719 .menu_button {
648 display: inline-block;720 display: inline-block;
649 height: min-content;721 height: min-content;
@@ -651,11 +723,19 @@
651 margin: 0 0.5em 0 0;723 margin: 0 0.5em 0 0;
652 }724 }
653 }725 }
726
654 .qr--id {727 .qr--id {
655 opacity: 0.5;728 opacity: 0.5;
656 &:before { content: "["; }729
657 &:after { content: "]"; }730 &:before {
731 content: "[";
732 }
733
734 &:after {
735 content: "]";
736 }
658 }737 }
738
659 .qr--message {739 .qr--message {
660 height: 1lh;740 height: 1lh;
661 overflow: hidden;741 overflow: hidden;
@@ -664,102 +744,124 @@
664 opacity: 0.5;744 opacity: 0.5;
665 }745 }
666 }746 }
667 }747 }
668 }748 }
669 > .qr--modal-messageContainer {749
670 flex: 1 1 auto;750 >.qr--modal-messageContainer {
671 display: flex;751 flex: 1 1 auto;
672 flex-direction: column;752 display: flex;
673 overflow: hidden;753 flex-direction: column;
674 > .qr--modal-editorSettings {754 overflow: hidden;
675 display: flex;755
756 >.qr--modal-editorSettings {
757 display: flex;
676 flex-wrap: wrap;758 flex-wrap: wrap;
677 flex-direction: row;759 flex-direction: row;
678 column-gap: 1em;760 column-gap: 1em;
679 color: var(--grey70);761 color: var(--grey70);
680 font-size: smaller;762 font-size: smaller;
681 align-items: baseline;763 align-items: baseline;
682 > .checkbox_label {764
683 white-space: nowrap;765 >.checkbox_label {
684 > input {766 white-space: nowrap;
685 font-size: inherit;767
686 }768 >input {
687 }769 font-size: inherit;
688 }770 }
689 > #qr--modal-messageHolder {771 }
690 flex: 1 1 auto;772 }
691 display: grid;773
692 text-align: left;774 >#qr--modal-messageHolder {
693 overflow: hidden;775 flex: 1 1 auto;
694 &.qr--noSyntax {776 display: grid;
695 > #qr--modal-messageSyntax {777 text-align: left;
696 display: none;778 overflow: hidden;
697 }779
698 > #qr--modal-message {780 &.qr--noSyntax {
699 background-color: var(--ac-style-color-background);781 >#qr--modal-messageSyntax {
700 color: var(--ac-style-color-text);782 display: none;
701 &::-webkit-scrollbar, &::-webkit-scrollbar-thumb {783 }
784
785 >#qr--modal-message {
786 background-color: var(--ac-style-color-background);
787 color: var(--ac-style-color-text);
788
789 &::-webkit-scrollbar,
790 &::-webkit-scrollbar-thumb {
702 visibility: visible;791 visibility: visible;
703 cursor: unset;792 cursor: unset;
704 }793 }
705 &::selection {794
706 color: unset;795 &::selection {
707 background-color: rgba(108 171 251 / 0.25);796 color: unset;
708 @supports (color: rgb(from white r g b / 0.25)) {797 background-color: rgba(108 171 251 / 0.25);
709 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);798
710 }799 @supports (color: rgb(from white r g b / 0.25)) {
711 }800 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);
712 }801 }
713 }802 }
714 > #qr--modal-messageSyntax {803 }
715 grid-column: 1;804 }
716 grid-row: 1;805
717 padding: 0;806 >#qr--modal-messageSyntax {
718 margin: 0;807 grid-column: 1;
719 border: none;808 grid-row: 1;
720 overflow: hidden;809 padding: 0;
721 min-width: 100%;810 margin: 0;
722 width: 0;811 border: none;
723 > #qr--modal-messageSyntaxInner {812 overflow: hidden;
724 height: 100%;813 min-width: 100%;
725 }814 width: 0;
726 }815
727 > #qr--modal-message {816 >#qr--modal-messageSyntaxInner {
728 background-color: transparent;817 height: 100%;
729 color: transparent;818 }
730 grid-column: 1;819 }
731 grid-row: 1;820
732 caret-color: var(--ac-style-color-text);821 >#qr--modal-message {
733 overflow: auto;822 background-color: transparent;
734 &::-webkit-scrollbar, &::-webkit-scrollbar-thumb {823 color: transparent;
735 visibility: hidden;824 grid-column: 1;
736 cursor: default;825 grid-row: 1;
737 }826 caret-color: var(--ac-style-color-text);
738 &::selection {827 overflow: auto;
739 color: transparent;828
740 background-color: rgba(108 171 251 / 0.25);829 &::-webkit-scrollbar,
741 @supports (color: rgb(from white r g b / 0.25)) {830 &::-webkit-scrollbar-thumb {
742 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);831 visibility: hidden;
743 }832 cursor: default;
744 }833 }
745 }834
746 #qr--modal-message, #qr--modal-messageSyntaxInner {835 &::selection {
747 font-family: var(--monoFontFamily);836 color: transparent;
748 padding: 0.75em;837 background-color: rgba(108 171 251 / 0.25);
749 margin: 0;838
750 resize: none;839 @supports (color: rgb(from white r g b / 0.25)) {
751 line-height: 1.2;840 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);
752 border: 1px solid var(--SmartThemeBorderColor);841 }
753 border-radius: 5px;842 }
754 position: relative;843 }
755 }844
756 }845 #qr--modal-message,
757 }846 #qr--modal-messageSyntaxInner {
758 }847 font-family: var(--monoFontFamily);
848 padding: 0.75em;
849 margin: 0;
850 resize: none;
851 line-height: 1.2;
852 border: 1px solid var(--SmartThemeBorderColor);
853 border-radius: 5px;
854 position: relative;
855 }
856 }
857 }
858 }
859
759 #qr--modal-icon {860 #qr--modal-icon {
760 height: 100%;861 height: 100%;
761 aspect-ratio: 1 / 1;862 aspect-ratio: 1 / 1;
762 }863 }
864
763 #qr--modal-executeButtons {865 #qr--modal-executeButtons {
764 display: flex;866 display: flex;
765 gap: 1em;867 gap: 1em;
@@ -778,7 +880,7 @@
778 }880 }
779881
780 #qr--modal-execute {882 #qr--modal-execute {
781 transition: 200ms;883 transition: var(--animation-duration-2x);
782 filter: grayscale(0);884 filter: grayscale(0);
783885
784 &.qr--busy {886 &.qr--busy {
@@ -819,16 +921,20 @@
819 border-color: rgb(215, 136, 114);921 border-color: rgb(215, 136, 114);
820 }922 }
821 }923 }
822 #qr--modal-debugButtons {924
823 display: none;925 #qr--modal-debugButtons {
824 gap: 1em;926 display: none;
825 .qr--modal-debugButton {927 gap: 1em;
826 aspect-ratio: 1.25 / 1;928
827 width: 2.25em;929 .qr--modal-debugButton {
930 aspect-ratio: 1.25 / 1;
931 width: 2.25em;
828 position: relative;932 position: relative;
933
829 &:not(.fa-solid) {934 &:not(.fa-solid) {
830 border-width: 1px;935 border-width: 1px;
831 border-style: solid;936 border-style: solid;
937
832 &:after {938 &:after {
833 content: '';939 content: '';
834 position: absolute;940 position: absolute;
@@ -839,23 +945,28 @@
839 mask-repeat: no-repeat;945 mask-repeat: no-repeat;
840 }946 }
841 }947 }
948
842 &#qr--modal-resume:after {949 &#qr--modal-resume:after {
843 mask-image: url('/img/step-resume.svg');950 mask-image: url('/img/step-resume.svg');
844 }951 }
952
845 &#qr--modal-step:after {953 &#qr--modal-step:after {
846 mask-image: url('/img/step-over.svg');954 mask-image: url('/img/step-over.svg');
847 }955 }
956
848 &#qr--modal-stepInto:after {957 &#qr--modal-stepInto:after {
849 mask-image: url('/img/step-into.svg');958 mask-image: url('/img/step-into.svg');
850 }959 }
960
851 &#qr--modal-stepOut:after {961 &#qr--modal-stepOut:after {
852 mask-image: url('/img/step-out.svg');962 mask-image: url('/img/step-out.svg');
853 }963 }
964
854 &#qr--modal-maximize {965 &#qr--modal-maximize {
855 display: none;966 display: none;
856 }967 }
857 }968 }
858 }969 }
859970
860 #qr--modal-send_textarea {971 #qr--modal-send_textarea {
861 flex: 0 0 auto;972 flex: 0 0 auto;
@@ -879,7 +990,7 @@
879 position: absolute;990 position: absolute;
880 inset: 0;991 inset: 0;
881 right: calc(100% - var(--prog) * 1%);992 right: calc(100% - var(--prog) * 1%);
882 transition: 200ms;993 transition: var(--animation-duration-2x);
883 }994 }
884995
885 &.qr--paused:after {996 &.qr--paused:after {
@@ -939,135 +1050,180 @@
939 overflow: auto;1050 overflow: auto;
940 min-width: 100%;1051 min-width: 100%;
941 width: 0;1052 width: 0;
942 white-space: pre-wrap;1053 white-space: pre-wrap;
943 }1054 }
944 #qr--modal-debugState {1055
945 display: none;1056 #qr--modal-debugState {
946 &.qr--active {1057 display: none;
947 display: block;1058
948 }1059 &.qr--active {
949 text-align: left;1060 display: block;
950 font-size: smaller;1061 }
951 font-family: var(--monoFontFamily);1062
952 // background-color: rgb(146, 190, 252);1063 text-align: left;
953 color: white;1064 font-size: smaller;
954 padding: 0.5em 0;1065 font-family: var(--monoFontFamily);
955 overflow: auto;1066 // background-color: rgb(146, 190, 252);
956 min-width: 100%;1067 color: white;
957 width: 0;1068 padding: 0.5em 0;
958 white-space: pre-wrap;1069 overflow: auto;
9591070 min-width: 100%;
960 .qr--scope {1071 width: 0;
961 display: grid;1072 white-space: pre-wrap;
962 grid-template-columns: 0fr 1fr 1fr;1073
963 column-gap: 0em;1074 .qr--scope {
964 .qr--title {1075 display: grid;
965 grid-column: 1 / 4;1076 grid-template-columns: 0fr 1fr 1fr;
966 font-weight: bold;1077 column-gap: 0em;
967 font-family: var(--mainFontFamily);1078
968 background-color: var(--black50a);1079 .qr--title {
969 padding: 0.25em;1080 grid-column: 1 / 4;
970 margin-top: 0.5em;1081 font-weight: bold;
971 }1082 font-family: var(--mainFontFamily);
972 .qr--var, .qr--macro, .qr--pipe {1083 background-color: var(--black50a);
973 display: contents;1084 padding: 0.25em;
974 &:nth-child(2n + 1) {1085 margin-top: 0.5em;
975 .qr--key, .qr--val {1086 }
976 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);1087
977 }1088 .qr--var,
978 .qr--val {1089 .qr--macro,
979 &:nth-child(2n) {1090 .qr--pipe {
980 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.125);1091 display: contents;
981 }1092
982 &:hover {1093 &:nth-child(2n + 1) {
983 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);1094
984 }1095 .qr--key,
985 }1096 .qr--val {
986 }1097 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
987 &:nth-child(2n) {1098 }
988 .qr--val {1099
989 &:nth-child(2n) {1100 .qr--val {
990 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.0625);1101 &:nth-child(2n) {
991 }1102 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.125);
992 &:hover {1103 }
993 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);1104
994 }1105 &:hover {
995 }1106 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);
996 }1107 }
997 &.qr--isHidden {1108 }
998 .qr--key, .qr--val {1109 }
999 opacity: 0.5;1110
1000 }1111 &:nth-child(2n) {
1001 }1112 .qr--val {
1002 .qr--val {1113 &:nth-child(2n) {
1003 grid-column: 2 / 4;1114 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.0625);
1004 &.qr--singleCol {1115 }
1005 grid-column: unset;1116
1006 }1117 &:hover {
1007 &.qr--simple {1118 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);
1008 &:before, &:after {1119 }
1009 content: '"';1120 }
1010 color: var(--SmartThemeQuoteColor);1121 }
1011 }1122
1012 }1123 &.qr--isHidden {
1013 &.qr--unresolved {1124
1014 &:after {1125 .qr--key,
1015 content: '-UNRESOLVED-';1126 .qr--val {
1016 font-style: italic;1127 opacity: 0.5;
1017 color: var(--SmartThemeQuoteColor);1128 }
1018 }1129 }
1019 }1130
1020 }1131 .qr--val {
1021 }1132 grid-column: 2 / 4;
1022 .qr--key {1133
1023 margin-left: 0.5em;1134 &.qr--singleCol {
1024 padding-right: 1em;1135 grid-column: unset;
1025 &:after { content: ": "; }1136 }
1026 }1137
1027 .qr--pipe, .qr--macro {1138 &.qr--simple {
1028 > .qr--key {1139
1029 &:before { content: "{{"; }1140 &:before,
1030 &:after { content: "}}: "; }1141 &:after {
1031 }1142 content: '"';
1032 }1143 color: var(--SmartThemeQuoteColor);
1033 .qr--scope {1144 }
1034 display: contents;1145 }
1035 .qr--pipe {1146
1036 .qr--key, .qr--val {1147 &.qr--unresolved {
1037 opacity: 0.5;1148 &:after {
1038 }1149 content: '-UNRESOLVED-';
1039 }1150 font-style: italic;
1040 }1151 color: var(--SmartThemeQuoteColor);
1041 }1152 }
10421153 }
1043 .qr--stack {1154 }
1155 }
1156
1157 .qr--key {
1158 margin-left: 0.5em;
1159 padding-right: 1em;
1160
1161 &:after {
1162 content: ": ";
1163 }
1164 }
1165
1166 .qr--pipe,
1167 .qr--macro {
1168 >.qr--key {
1169 &:before {
1170 content: "{{";
1171 }
1172
1173 &:after {
1174 content: "}}: ";
1175 }
1176 }
1177 }
1178
1179 .qr--scope {
1180 display: contents;
1181
1182 .qr--pipe {
1183
1184 .qr--key,
1185 .qr--val {
1186 opacity: 0.5;
1187 }
1188 }
1189 }
1190 }
1191
1192 .qr--stack {
1044 display: grid;1193 display: grid;
1045 grid-template-columns: 1fr 0fr;1194 grid-template-columns: 1fr 0fr;
1046 .qr--title {1195
1047 grid-column: 1 / 3;1196 .qr--title {
1048 font-weight: bold;1197 grid-column: 1 / 3;
1049 font-family: var(--mainFontFamily);1198 font-weight: bold;
1050 background-color: var(--black50a);1199 font-family: var(--mainFontFamily);
1051 padding: 0.25em;1200 background-color: var(--black50a);
1052 margin-top: 1em;1201 padding: 0.25em;
1053 }1202 margin-top: 1em;
1054 .qr--item {1203 }
1204
1205 .qr--item {
1055 display: contents;1206 display: contents;
1056 &:nth-child(2n + 1) {1207
1057 .qr--name, .qr--source {1208 &:nth-child(2n + 1) {
1209
1210 .qr--name,
1211 .qr--source {
1058 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);1212 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
1059 }1213 }
1060 }1214 }
1215
1061 .qr--name {1216 .qr--name {
1062 margin-left: 0.5em;1217 margin-left: 0.5em;
1063 }1218 }
1219
1064 .qr--source {1220 .qr--source {
1065 opacity: 0.5;1221 opacity: 0.5;
1066 text-align: right;1222 text-align: right;
1067 white-space: nowrap;1223 white-space: nowrap;
1068 }1224 }
1069 }1225 }
1070 }1226 }
1071 }1227 }
1072 }1228 }
1073 }1229 }
@@ -1086,6 +1242,7 @@
1086}1242}
10871243
1088@keyframes qr--debugPulse {1244@keyframes qr--debugPulse {
1245
1089 0%,1246 0%,
1090 100% {1247 100% {
1091 border-color: rgb(81, 163, 81);1248 border-color: rgb(81, 163, 81);
@@ -1097,10 +1254,11 @@
1097}1254}
10981255
1099.popup.qr--hide {1256.popup.qr--hide {
1100 opacity: 0 !important;1257 opacity: 0 !important;
1101 &::backdrop {1258
1102 opacity: 0 !important;1259 &::backdrop {
1103 }1260 opacity: 0 !important;
1261 }
1104}1262}
11051263
1106.popup.qr--hide::backdrop {1264.popup.qr--hide::backdrop {
@@ -1110,50 +1268,56 @@
11101268
11111269
1112.popup:has(.qr--transferModal) {1270.popup:has(.qr--transferModal) {
1113 .popup-button-ok {1271 .popup-button-ok {
1114 &:after {1272 &:after {
1115 content: 'Transfer';1273 content: 'Transfer';
1116 height: 0;1274 height: 0;
1117 overflow: hidden;1275 overflow: hidden;
1118 font-weight: bold;1276 font-weight: bold;
1119 }1277 }
1120 display: flex;1278
1121 align-items: center;1279 display: flex;
1122 flex-direction: column;1280 align-items: center;
1123 white-space: pre;1281 flex-direction: column;
1124 font-weight: normal;1282 white-space: pre;
1125 box-shadow: 0 0 0;1283 font-weight: normal;
1126 transition: 200ms;1284 box-shadow: 0 0 0;
1127 }1285 transition: var(--animation-duration-2x);
1128 .qr--copy {1286 }
1129 &:after {1287
1130 content: 'Copy';1288 .qr--copy {
1131 height: 0;1289 &:after {
1132 overflow: hidden;1290 content: 'Copy';
1133 font-weight: bold;1291 height: 0;
1134 }1292 overflow: hidden;
1135 display: flex;1293 font-weight: bold;
1136 align-items: center;1294 }
1137 flex-direction: column;1295
1138 white-space: pre;1296 display: flex;
1139 font-weight: normal;1297 align-items: center;
1140 box-shadow: 0 0 0;1298 flex-direction: column;
1141 transition: 200ms;1299 white-space: pre;
1142 }1300 font-weight: normal;
1143 &:has(.qr--transferSelect:focus) {1301 box-shadow: 0 0 0;
1144 .popup-button-ok {1302 transition: var(--animation-duration-2x);
1145 font-weight: bold;1303 }
1146 box-shadow: 0 0 10px;1304
1147 }1305 &:has(.qr--transferSelect:focus) {
1148 &.qr--isCopy {1306 .popup-button-ok {
1149 .popup-button-ok {1307 font-weight: bold;
1150 font-weight: normal;1308 box-shadow: 0 0 10px;
1151 box-shadow: 0 0 0;1309 }
1152 }1310
1153 .qr--copy {1311 &.qr--isCopy {
1154 font-weight: bold;1312 .popup-button-ok {
1155 box-shadow: 0 0 10px;1313 font-weight: normal;
1156 }1314 box-shadow: 0 0 0;
1157 }1315 }
1158 }1316
1317 .qr--copy {
1318 font-weight: bold;
1319 box-shadow: 0 0 10px;
1320 }
1321 }
1322 }
1159}1323}
public/scripts/extensions/regex/style.css+2 -2
@@ -24,7 +24,7 @@
2424
25#scoped_scripts_block {25#scoped_scripts_block {
26 opacity: 1;26 opacity: 1;
27 transition: opacity 0.2s ease-in-out;27 transition: opacity var(--animation-duration-2x) ease-in-out;
28}28}
2929
30#scoped_scripts_block .move_to_scoped {30#scoped_scripts_block .move_to_scoped {
@@ -78,7 +78,7 @@ input.enable_scoped {
78 cursor: pointer;78 cursor: pointer;
79 opacity: 0.5;79 opacity: 0.5;
80 filter: grayscale(0.5);80 filter: grayscale(0.5);
81 transition: opacity 0.2s ease-in-out;81 transition: opacity var(--animation-duration-2x) ease-in-out;
82}82}
8383
84.regex-toggle-off:hover {84.regex-toggle-off:hover {
public/scripts/extensions/tts/style.css+1 -1
@@ -25,7 +25,7 @@
25 outline: none;25 outline: none;
26 border: none;26 border: none;
27 cursor: pointer;27 cursor: pointer;
28 transition: 0.3s;28 transition: var(--animation-duration-2x);
29 opacity: 0.7;29 opacity: 0.7;
30 align-items: center;30 align-items: center;
31 justify-content: center;31 justify-content: center;
public/scripts/utils.js+8 -6
@@ -2153,20 +2153,22 @@ export function getFreeName(name, list, numberFormatter = (n) => ` #${n}`) {
2153 */2153 */
2154export function toggleDrawer(drawer, expand = true) {2154export function toggleDrawer(drawer, expand = true) {
2155 /** @type {HTMLElement} */2155 /** @type {HTMLElement} */
2156 const icon = drawer.querySelector('.inline-drawer-icon');2156 const icon = drawer.querySelector(':scope > .inline-drawer-header .inline-drawer-icon');
2157 /** @type {HTMLElement} */2157 /** @type {HTMLElement} */
2158 const content = drawer.querySelector('.inline-drawer-content');2158 const content = drawer.querySelector(':scope > .inline-drawer-content');
21592159
2160 if (expand) {2160 if (expand) {
2161 icon.classList.remove('up', 'fa-circle-chevron-up');
2162 icon.classList.add('down', 'fa-circle-chevron-down');
2163 content.style.display = 'block';
2164 } else {
2165 icon.classList.remove('down', 'fa-circle-chevron-down');2161 icon.classList.remove('down', 'fa-circle-chevron-down');
2166 icon.classList.add('up', 'fa-circle-chevron-up');2162 icon.classList.add('up', 'fa-circle-chevron-up');
2163 content.style.display = 'block';
2164 } else {
2165 icon.classList.remove('up', 'fa-circle-chevron-up');
2166 icon.classList.add('down', 'fa-circle-chevron-down');
2167 content.style.display = 'none';2167 content.style.display = 'none';
2168 }2168 }
21692169
2170 drawer.dispatchEvent(new CustomEvent('inline-drawer-toggle', { bubbles: true }));
2171
2170 // Set the height of "autoSetHeight" textareas within the inline-drawer to their scroll height2172 // Set the height of "autoSetHeight" textareas within the inline-drawer to their scroll height
2171 if (!CSS.supports('field-sizing', 'content')) {2173 if (!CSS.supports('field-sizing', 'content')) {
2172 content.querySelectorAll('textarea.autoSetHeight').forEach(resetScrollHeight);2174 content.querySelectorAll('textarea.autoSetHeight').forEach(resetScrollHeight);
public/scripts/world-info.js+683 -752
@@ -58,7 +58,8 @@ export const scan_state = {
58 MIN_ACTIVATIONS: 3,58 MIN_ACTIVATIONS: 3,
59};59};
6060
61const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry');61const WI_ENTRY_HEADER_TEMPLATE = $('#entry_edit_template .world_entry');
62const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry_edit');
6263
63export let world_info = {};64export let world_info = {};
64export let selected_world_info = [];65export let selected_world_info = [];
@@ -85,6 +86,7 @@ const saveSettingsDebounced = debounce(() => {
85}, debounce_timeout.relaxed);86}, debounce_timeout.relaxed);
86const sortFn = (a, b) => b.order - a.order;87const sortFn = (a, b) => b.order - a.order;
87let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); };88let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); };
89let isSaveWorldInfoDisabled = false;
8890
89// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.91// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data.
90export const worldInfoFilter = new FilterHelper(() => updateEditor());92export const worldInfoFilter = new FilterHelper(() => updateEditor());
@@ -965,6 +967,7 @@ export function reloadEditor(file, loadIfNotSelected = false) {
965 }967 }
966}968}
967969
970//MARK: regWISlashCommands
968function registerWorldInfoSlashCommands() {971function registerWorldInfoSlashCommands() {
969 /**972 /**
970 * Gets a *rough* approximation of the current chat context.973 * Gets a *rough* approximation of the current chat context.
@@ -1754,12 +1757,12 @@ function registerWorldInfoSlashCommands() {
1754 */1757 */
1755export async function showWorldEditor(name) {1758export async function showWorldEditor(name) {
1756 if (!name) {1759 if (!name) {
1757 hideWorldEditor();1760 await hideWorldEditor();
1758 return;1761 return;
1759 }1762 }
17601763
1761 const wiData = await loadWorldInfo(name);1764 const wiData = await loadWorldInfo(name);
1762 displayWorldEntries(name, wiData);1765 await displayWorldEntries(name, wiData);
1763}1766}
17641767
1765/**1768/**
@@ -1815,8 +1818,8 @@ export async function updateWorldInfoList() {
1815 }1818 }
1816}1819}
18171820
1818function hideWorldEditor() {1821async function hideWorldEditor() {
1819 displayWorldEntries(null, null);1822 await displayWorldEntries(null, null);
1820}1823}
18211824
1822function getWIElement(name) {1825function getWIElement(name) {
@@ -1937,15 +1940,72 @@ function updateWorldEntryKeyOptionsCache(keyOptions, { remove = false, reset = f
1937 worldEntryKeyOptionsCache.sort((a, b) => b.count - a.count || a.text.localeCompare(b.text));1940 worldEntryKeyOptionsCache.sort((a, b) => b.count - a.count || a.text.localeCompare(b.text));
1938}1941}
19391942
1940function displayWorldEntries(name, data, navigation = navigation_option.none, flashOnNav = true) {1943function clearEntryList($list) {
1941 updateEditor = (navigation, flashOnNav = true) => displayWorldEntries(name, data, navigation, flashOnNav);1944 console.time('clearEntryList');
19421945
1943 const worldEntriesList = $('#world_popup_entries_list');1946 if (!$list.children().length) {
1947 console.debug('List already empty, skipping cleanup.');
1948 console.timeEnd('clearEntryList');
1949 return;
1950 }
1951
1952 // Step 1: Clean all <option> elements within <select>
1953 $list.find('option').each(function () {
1954 const $option = $(this);
1955 $option.off();
1956 $.cleanData([$option[0]]);
1957 $option.remove();
1958 });
19441959
1945 // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements1960 // Step 2: Clean all <select> elements
1946 // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it1961 $list.find('select').each(function () {
1947 worldEntriesList.find('*').off();1962 const $select = $(this);
1948 worldEntriesList.empty().show();1963 // Remove Select2-related data and container if present
1964 if ($select.data('select2')) {
1965 try {
1966 $select.select2('destroy');
1967 } catch (e) {
1968 console.debug('Select2 destroy failed:', e);
1969 }
1970 }
1971 const $container = $select.parent();
1972 if ($container.length) {
1973 $container.find('*').off();
1974 $.cleanData($container.find('*').get());
1975 $container.remove();
1976 }
1977
1978 $select.off();
1979 $.cleanData([$select[0]]);
1980 });
1981
1982 // Step 3: Clean <div>, <span>, <input>
1983 $list.find('div, span, input').each(function () {
1984 const $elem = $(this);
1985 $elem.off();
1986 $.cleanData([$elem[0]]);
1987 $elem.remove();
1988 });
1989
1990 const totalElementsOfAnyKindLeftInList = $list.children().length;
1991
1992 // Final cleanup
1993 if (totalElementsOfAnyKindLeftInList) {
1994 console.time('empty');
1995 $list.empty();
1996 console.timeEnd('empty');
1997 }
1998
1999 console.timeEnd('clearEntryList');
2000}
2001
2002//MARK: displayWorldEntries
2003async function displayWorldEntries(name, data, navigation = navigation_option.none, flashOnNav = true) {
2004 updateEditor = async (navigation, flashOnNav = true) => await displayWorldEntries(name, data, navigation, flashOnNav);
2005
2006 const worldEntriesList = $('#world_popup_entries_list');
2007 clearEntryList(worldEntriesList);
2008 worldEntriesList.show();
19492009
1950 if (!data || !('entries' in data)) {2010 if (!data || !('entries' in data)) {
1951 $('#world_popup_new').off('click').on('click', nullWorldInfo);2011 $('#world_popup_new').off('click').on('click', nullWorldInfo);
@@ -2038,22 +2098,39 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
2038 formatNavigator: PAGINATION_TEMPLATE,2098 formatNavigator: PAGINATION_TEMPLATE,
2039 showNavigator: true,2099 showNavigator: true,
2040 callback: async function (/** @type {object[]} */ page) {2100 callback: async function (/** @type {object[]} */ page) {
2041 // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements2101 try {
2042 // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it2102 // Prevent saveWorldInfo from firing timeouts while rendering the list
2043 worldEntriesList.find('*').off();2103 isSaveWorldInfoDisabled = true;
2044 worldEntriesList.empty();2104 clearEntryList(worldEntriesList);
20452105
2046 const keywordHeaders = await renderTemplateAsync('worldInfoKeywordHeaders');2106 const keywordHeaders = await renderTemplateAsync('worldInfoKeywordHeaders');
2047 const blocksPromises = page.map(async (entry) => await getWorldEntry(name, data, entry)).filter(x => x);2107 const blocks = [];
2048 const blocks = await Promise.all(blocksPromises);2108
2049 const isCustomOrder = $('#world_info_sort_order').find(':selected').data('rule') === 'custom';2109 for (const entry of page) {
2050 if (!isCustomOrder) {2110 try {
2051 blocks.forEach(block => {2111 const block = await getWorldEntry(name, data, entry);
2052 block.find('.drag-handle').remove();2112 if (block) {
2053 });2113 blocks.push(block);
2114 }
2115 } catch (error) {
2116 console.error(`Error while processing entry ${entry.uid}:`, error);
2117 }
2118 }
2119
2120 const isCustomOrder = $('#world_info_sort_order').find(':selected').data('rule') === 'custom';
2121 if (!isCustomOrder) {
2122 blocks.forEach(block => {
2123 block.find('.drag-handle').remove();
2124 });
2125 }
2126
2127 worldEntriesList.append(keywordHeaders);
2128 worldEntriesList.append(blocks);
2129 } catch (error) {
2130 console.error('Error while rendering WI entries:', error);
2131 } finally {
2132 isSaveWorldInfoDisabled = false;
2054 }2133 }
2055 worldEntriesList.append(keywordHeaders);
2056 worldEntriesList.append(blocks);
2057 },2134 },
2058 afterSizeSelectorChange: function (e) {2135 afterSizeSelectorChange: function (e) {
2059 accountStorage.setItem(storageKey, e.target.value);2136 accountStorage.setItem(storageKey, e.target.value);
@@ -2173,7 +2250,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
2173 if (selectedIndex !== -1) {2250 if (selectedIndex !== -1) {
2174 $('#world_editor_select').val(selectedIndex).trigger('change');2251 $('#world_editor_select').val(selectedIndex).trigger('change');
2175 } else {2252 } else {
2176 hideWorldEditor();2253 await hideWorldEditor();
2177 }2254 }
2178 }2255 }
2179 });2256 });
@@ -2211,6 +2288,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
2211 await saveWorldInfo(name, data);2288 await saveWorldInfo(name, data);
2212 },2289 },
2213 });2290 });
2291
2214 //$("#world_popup_entries_list").disableSelection();2292 //$("#world_popup_entries_list").disableSelection();
2215}2293}
22162294
@@ -2453,208 +2531,137 @@ export function parseRegexFromString(input) {
2453 }2531 }
2454}2532}
24552533
2456export async function getWorldEntry(name, data, entry) {2534//MARK: getWorldEntry
2457 if (!data.entries[entry.uid]) {2535function enableKeysInputHelper({ template, entry, entryPropName, originalDataValueName, name, data }) {
2458 return;2536 const isFancyInput = !isMobile() && !power_user.wi_key_input_plaintext;
2459 }2537 const input = isFancyInput ? template.find(`select[name="${entryPropName}"]`) : template.find(`textarea[name="${entryPropName}"]`);
24602538 input.data('uid', entry.uid);
2461 const template = WI_ENTRY_EDIT_TEMPLATE.clone();2539 input.on('click', function (event) {
2462 template.data('uid', entry.uid);2540 event.stopPropagation();
2463 template.attr('uid', entry.uid);2541 });
2464
2465 // Init default state of WI Key toggle (=> true)
2466 if (typeof power_user.wi_key_input_plaintext === 'undefined') power_user.wi_key_input_plaintext = true;
24672542
2468 /** Function to build the keys input controls @param {string} entryPropName @param {string} originalDataValueName */2543 function templateStyling(item, { searchStyle = false } = {}) {
2469 function enableKeysInput(entryPropName, originalDataValueName) {2544 const content = $('<span>').addClass('item').text(item.text).attr('title', `${item.text}\n\nClick to edit`);
2470 const isFancyInput = !isMobile() && !power_user.wi_key_input_plaintext;2545 const isRegex = isValidRegex(item.text);
2471 const input = isFancyInput ? template.find(`select[name="${entryPropName}"]`) : template.find(`textarea[name="${entryPropName}"]`);2546 if (isRegex) {
2472 input.data('uid', entry.uid);2547 content.html(highlightRegex(item.text));
2473 input.on('click', function (event) {2548 content.addClass('regex_item').prepend($('<span>').addClass('regex_icon').text('•*').attr('title', 'Regex'));
2474 // Prevent closing the drawer on clicking the input2549 }
2475 event.stopPropagation();2550 if (searchStyle && item.count) {
2551 const wrapper = $('<span>').addClass('result_block').append(content);
2552 wrapper.append($('<span>').addClass('item_count').text(item.count).attr('title', `Used as a key ${item.count} ${item.count != 1 ? 'times' : 'time'} in this lorebook`));
2553 return wrapper;
2554 }
2555 return content;
2556 }
2557
2558 if (isFancyInput) {
2559 select2ModifyOptions(input, entry[entryPropName], { select: true, changeEventArgs: { skipReset: true, noSave: true } });
2560 input.select2({
2561 ajax: dynamicSelect2DataViaAjax(() => worldEntryKeyOptionsCache),
2562 tags: true,
2563 tokenSeparators: [','],
2564 tokenizer: customTokenizer,
2565 placeholder: input.attr('placeholder'),
2566 templateResult: item => templateStyling(item, { searchStyle: true }),
2567 templateSelection: item => templateStyling(item),
2476 });2568 });
24772569
2478 function templateStyling(/** @type {Select2Option} */ item, { searchStyle = false } = {}) {2570 // TypeScript-safe event handler
2479 const content = $('<span>').addClass('item').text(item.text).attr('title', `${item.text}\n\nClick to edit`);2571 /**
2480 const isRegex = isValidRegex(item.text);2572 * @param {Event} _event
2481 if (isRegex) {2573 * @param {{ skipReset?: boolean, noSave?: boolean }} [arg]
2482 content.html(highlightRegex(item.text));2574 */
2483 content.addClass('regex_item').prepend($('<span>').addClass('regex_icon').text('•*').attr('title', 'Regex'));2575 input.on('change', async function (_event, arg) {
2484 }2576 const uid = $(this).data('uid');
24852577 const keys = ($(this).select2('data')).map(x => x.text);
2486 if (searchStyle && item.count) {2578 const skipReset = arg?.skipReset ?? false;
2487 // Build a wrapping element2579 const noSave = arg?.noSave ?? false;
2488 const wrapper = $('<span>').addClass('result_block')2580 if (!skipReset) await resetScrollHeight(this);
2489 .append(content);2581 if (!noSave) {
2490 wrapper.append($('<span>').addClass('item_count').text(item.count).attr('title', `Used as a key ${item.count} ${item.count != 1 ? 'times' : 'time'} in this lorebook`));2582 data.entries[uid][entryPropName] = keys;
2491 return wrapper;2583 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
2584 await saveWorldInfo(name, data);
2492 }2585 }
2586 $(this).toggleClass('empty', !data.entries[uid][entryPropName].length);
2587 });
24932588
2494 return content;2589 input.toggleClass('empty', !entry[entryPropName].length);
2495 }2590 input.on('select2:select', event => updateWorldEntryKeyOptionsCache([event.params.data]));
24962591 input.on('select2:unselect', event => updateWorldEntryKeyOptionsCache([event.params.data], { remove: true }));
2497 if (isFancyInput) {2592
2498 // First initialize existing values as options, before initializing select2, to speed up performance2593 select2ChoiceClickSubscribe(input, target => {
2499 select2ModifyOptions(input, entry[entryPropName], { select: true, changeEventArgs: { skipReset: true, noSave: true } });2594 const key = $(target.closest('.regex-highlight, .item')).text();
25002595 const selected = input.val();
2501 input.select2({2596 if (!Array.isArray(selected)) return;
2502 ajax: dynamicSelect2DataViaAjax(() => worldEntryKeyOptionsCache),2597 var index = selected.indexOf(getSelect2OptionId(key));
2503 tags: true,2598 if (index > -1) selected.splice(index, 1);
2504 tokenSeparators: [','],2599 input.val(selected).trigger('change');
2505 tokenizer: customTokenizer,2600 updateWorldEntryKeyOptionsCache([key], { remove: true });
2506 placeholder: input.attr('placeholder'),2601 input.next('span.select2-container').find('textarea').val(key).trigger('input');
2507 templateResult: item => templateStyling(item, { searchStyle: true }),2602 }, { openDrawer: true });
2508 templateSelection: item => templateStyling(item),2603 } else {
2509 });2604 template.find(`select[name="${entryPropName}"]`).hide();
2510 input.on('change', async function (_, { skipReset, noSave } = {}) {2605 input.show();
2511 const uid = $(this).data('uid');2606 /**
2512 /** @type {string[]} */2607 * @param {Event} _event
2513 const keys = ($(this).select2('data')).map(x => x.text);2608 * @param {{ skipReset?: boolean, noSave?: boolean }} [arg]
25142609 */
2515 !skipReset && await resetScrollHeight(this);2610 input.on('change', async function (_event, arg) {
2516 if (!noSave) {2611 const uid = $(this).data('uid');
2517 data.entries[uid][entryPropName] = keys;2612 const value = String($(this).val());
2518 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);2613 const skipReset = arg?.skipReset ?? false;
2519 await saveWorldInfo(name, data);2614 const noSave = arg?.noSave ?? false;
2520 }2615 if (!skipReset) await resetScrollHeight(this);
2616 if (!noSave) {
2617 data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value);
2618 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
2619 await saveWorldInfo(name, data);
2521 $(this).toggleClass('empty', !data.entries[uid][entryPropName].length);2620 $(this).toggleClass('empty', !data.entries[uid][entryPropName].length);
2522 });2621 }
2523 input.toggleClass('empty', !entry[entryPropName].length);2622 });
2524 input.on('select2:select', /** @type {function(*):void} */ event => updateWorldEntryKeyOptionsCache([event.params.data]));2623 input.val(entry[entryPropName].join(', ')).trigger('input', { skipReset: true });
2525 input.on('select2:unselect', /** @type {function(*):void} */ event => updateWorldEntryKeyOptionsCache([event.params.data], { remove: true }));
2526
2527 select2ChoiceClickSubscribe(input, target => {
2528 const key = $(target.closest('.regex-highlight, .item')).text();
2529 console.debug('Editing WI key', key);
2530
2531 // Remove the current key from the actual selection
2532 const selected = input.val();
2533 if (!Array.isArray(selected)) return;
2534 var index = selected.indexOf(getSelect2OptionId(key));
2535 if (index > -1) selected.splice(index, 1);
2536 input.val(selected).trigger('change');
2537 // Manually update the cache, that change event is not gonna trigger it
2538 updateWorldEntryKeyOptionsCache([key], { remove: true });
2539
2540 // We need to "hack" the actual text input into the currently open textarea
2541 input.next('span.select2-container').find('textarea')
2542 .val(key).trigger('input');
2543 }, { openDrawer: true });
2544 }
2545 else {
2546 // Compatibility with mobile devices. On mobile we need a text input field, not a select option control, so we need its own event handlers
2547 template.find(`select[name="${entryPropName}"]`).hide();
2548 input.show();
2549
2550 input.on('input', async function (_, { skipReset, noSave } = {}) {
2551 const uid = $(this).data('uid');
2552 const value = String($(this).val());
2553 !skipReset && await resetScrollHeight(this);
2554 if (!noSave) {
2555 data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value);
2556 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
2557 await saveWorldInfo(name, data);
2558 $(this).toggleClass('empty', !data.entries[uid][entryPropName].length);
2559 }
2560 });
2561 input.val(entry[entryPropName].join(', ')).trigger('input', { skipReset: true });
2562 }
2563 return { isFancy: isFancyInput, control: input };
2564 }2624 }
2625 return { isFancy: isFancyInput, control: input };
2626}
25652627
2566 // key2628/**
2567 const keyInput = enableKeysInput('key', 'keys');2629 * Helper to handle match checkboxes for WI entries.
25682630 */
2569 // keysecondary2631function handleMatchCheckboxHelper({ template, entry, fieldName, data, name }) {
2570 const keySecondaryInput = enableKeysInput('keysecondary', 'secondary_keys');2632 const key = originalWIDataKeyMap[fieldName];
25712633 const checkBoxElem = template.find(`input[type="checkbox"][name="${fieldName}"]`);
2572 // draw key input switch button2634 checkBoxElem.data('uid', entry.uid);
2573 template.find('.switch_input_type_icon').on('click', function () {2635 checkBoxElem.on('input', async function () {
2574 power_user.wi_key_input_plaintext = !power_user.wi_key_input_plaintext;
2575 saveSettingsDebounced();
2576
2577 // Just redraw the panel
2578 const uid = ($(this).parents('.world_entry')).data('uid');
2579 updateEditor(uid, false);
2580
2581 $(`.world_entry[uid="${uid}"] .inline-drawer-icon`).trigger('click');
2582 // setTimeout(() => {
2583 // }, debounce_timeout.standard);
2584 }).each((_, icon) => {
2585 $(icon).attr('title', $(icon).data(power_user.wi_key_input_plaintext ? 'tooltip-on' : 'tooltip-off'));
2586 $(icon).text($(icon).data(power_user.wi_key_input_plaintext ? 'icon-on' : 'icon-off'));
2587 });
2588
2589 // logic AND/NOT
2590 const selectiveLogicDropdown = template.find('select[name="entryLogicType"]');
2591 selectiveLogicDropdown.data('uid', entry.uid);
2592
2593 selectiveLogicDropdown.on('click', function (event) {
2594 event.stopPropagation();
2595 });
2596
2597 selectiveLogicDropdown.on('input', async function () {
2598 const uid = $(this).data('uid');
2599 const value = Number($(this).val());
2600 data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY;
2601 setWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic);
2602 await saveWorldInfo(name, data);
2603 });
2604
2605 template
2606 .find(`select[name="entryLogicType"] option[value=${entry.selectiveLogic}]`)
2607 .prop('selected', true)
2608 .trigger('input');
2609
2610 // Character filter
2611 const characterFilterLabel = template.find('label[for="characterFilter"] > small');
2612 characterFilterLabel.text(entry.characterFilter?.isExclude ? 'Exclude Character(s)' : 'Filter to Character(s)');
2613
2614 // exclude characters checkbox
2615 const characterExclusionInput = template.find('input[name="character_exclusion"]');
2616 characterExclusionInput.data('uid', entry.uid);
2617 characterExclusionInput.on('input', async function () {
2618 const uid = $(this).data('uid');2636 const uid = $(this).data('uid');
2619 const value = $(this).prop('checked');2637 const value = $(this).prop('checked');
2620 characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)');2638 data.entries[uid][fieldName] = value;
2621 if (data.entries[uid].characterFilter) {2639 setWIOriginalDataValue(data, uid, key, data.entries[uid][fieldName]);
2622 if (!value && data.entries[uid].characterFilter.names.length === 0 && data.entries[uid].characterFilter.tags.length === 0) {
2623 delete data.entries[uid].characterFilter;
2624 } else {
2625 data.entries[uid].characterFilter.isExclude = value;
2626 }
2627 } else if (value) {
2628 Object.assign(
2629 data.entries[uid],
2630 {
2631 characterFilter: {
2632 isExclude: true,
2633 names: [],
2634 tags: [],
2635 },
2636 },
2637 );
2638 }
2639
2640 // Verify names to exist in the system
2641 if (data.entries[uid]?.characterFilter?.names?.length > 0) {
2642 for (const name of [...data.entries[uid].characterFilter.names]) {
2643 if (!getContext().characters.find(x => x.avatar.replace(/\.[^/.]+$/, '') === name)) {
2644 console.warn(`World Info: Character ${name} not found. Removing from the entry filter.`, entry);
2645 data.entries[uid].characterFilter.names = data.entries[uid].characterFilter.names.filter(x => x !== name);
2646 }
2647 }
2648 }
2649
2650 setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);
2651 await saveWorldInfo(name, data);2640 await saveWorldInfo(name, data);
2652 });2641 });
2653 characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input');2642 checkBoxElem.prop('checked', !!entry[fieldName]).trigger('input');
2643}
26542644
2655 const characterFilter = template.find('select[name="characterFilter"]');2645/**
2656 characterFilter.data('uid', entry.uid);2646 * Helper to update position/order display.
2647 */
2648function updatePosOrdDisplayHelper({ template, data, uid }) {
2649 let entry = data.entries[uid];
2650 let posText = entry.position;
2651 switch (entry.position) {
2652 case 0: posText = '↑CD'; break;
2653 case 1: posText = 'CD↓'; break;
2654 case 2: posText = '↑AN'; break;
2655 case 3: posText = 'AN↓'; break;
2656 case 4: posText = `@D${entry.depth}`; break;
2657 }
2658 template.find('.world_entry_form_position_value').text(`(${posText} ${entry.order})`);
2659}
26572660
2661/**
2662 * Helper to initialize character filter select2.
2663 */
2664function initCharacterFilterSelect2Helper(characterFilter, t) {
2658 if (!isMobile()) {2665 if (!isMobile()) {
2659 $(characterFilter).select2({2666 $(characterFilter).select2({
2660 width: '100%',2667 width: '100%',
@@ -2663,7 +2670,12 @@ export async function getWorldEntry(name, data, entry) {
2663 closeOnSelect: false,2670 closeOnSelect: false,
2664 });2671 });
2665 }2672 }
2673}
26662674
2675/**
2676 * Helper to fill character and tag options for character filter.
2677 */
2678function fillCharacterAndTagOptionsHelper({ characterFilter, entry, getContext }) {
2667 const characters = getContext().characters;2679 const characters = getContext().characters;
2668 characters.forEach((character) => {2680 characters.forEach((character) => {
2669 const option = document.createElement('option');2681 const option = document.createElement('option');
@@ -2673,7 +2685,6 @@ export async function getWorldEntry(name, data, entry) {
2673 option.setAttribute('data-type', 'character');2685 option.setAttribute('data-type', 'character');
2674 characterFilter.append(option);2686 characterFilter.append(option);
2675 });2687 });
2676
2677 const tags = getContext().tags;2688 const tags = getContext().tags;
2678 tags.forEach((tag) => {2689 tags.forEach((tag) => {
2679 const option = document.createElement('option');2690 const option = document.createElement('option');
@@ -2683,14 +2694,17 @@ export async function getWorldEntry(name, data, entry) {
2683 option.setAttribute('data-type', 'tag');2694 option.setAttribute('data-type', 'tag');
2684 characterFilter.append(option);2695 characterFilter.append(option);
2685 });2696 });
2697}
26862698
2699/**
2700 * Helper to handle character filter changes.
2701 */
2702function handleCharacterFilterChangeHelper({ characterFilter, data, entry, name, world_names, getContext, setWIOriginalDataValue, saveWorldInfo, t }) {
2687 characterFilter.on('mousedown change', async function (e) {2703 characterFilter.on('mousedown change', async function (e) {
2688 // If there's no world names, don't do anything
2689 if (world_names.length === 0) {2704 if (world_names.length === 0) {
2690 e.preventDefault();2705 e.preventDefault();
2691 return;2706 return;
2692 }2707 }
2693
2694 const uid = $(this).data('uid');2708 const uid = $(this).data('uid');
2695 const selected = $(this).find(':selected');2709 const selected = $(this).find(':selected');
2696 if ((!selected || selected?.length === 0) && !data.entries[uid].characterFilter?.isExclude) {2710 if ((!selected || selected?.length === 0) && !data.entries[uid].characterFilter?.isExclude) {
@@ -2712,361 +2726,99 @@ export async function getWorldEntry(name, data, entry) {
2712 setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);2726 setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);
2713 await saveWorldInfo(name, data);2727 await saveWorldInfo(name, data);
2714 });2728 });
2729}
27152730
2716 // comment2731/**
2717 const commentInput = template.find('textarea[name="comment"]');2732 * Helper to handle probability input.
2718 const commentToggle = template.find('input[name="addMemo"]');2733 */
2719 commentInput.data('uid', entry.uid);2734function handleProbabilityInputHelper({ probabilityInput, data, entry, name, setWIOriginalDataValue, saveWorldInfo }) {
2720 commentInput.on('input', async function (_, { skipReset } = {}) {
2721 const uid = $(this).data('uid');
2722 const value = $(this).val();
2723 !skipReset && await resetScrollHeight(this);
2724 data.entries[uid].comment = value;
2725
2726 setWIOriginalDataValue(data, uid, 'comment', data.entries[uid].comment);
2727 await saveWorldInfo(name, data);
2728 });
2729 commentToggle.data('uid', entry.uid);
2730 commentToggle.on('input', async function () {
2731 const uid = $(this).data('uid');
2732 const value = $(this).prop('checked');
2733 //console.log(value)
2734 const commentContainer = $(this)
2735 .closest('.world_entry')
2736 .find('.commentContainer');
2737 data.entries[uid].addMemo = value;
2738 await saveWorldInfo(name, data);
2739 value ? commentContainer.show() : commentContainer.hide();
2740 });
2741
2742 commentInput.val(entry.comment).trigger('input', { skipReset: true });
2743 //initScrollHeight(commentInput);
2744 commentToggle.prop('checked', true /* entry.addMemo */).trigger('input');
2745 commentToggle.parent().hide();
2746
2747 // content
2748 const counter = template.find('.world_entry_form_token_counter');
2749 const countTokensDebounced = debounce(async function (counter, value) {
2750 const numberOfTokens = await getTokenCountAsync(value);
2751 $(counter).text(numberOfTokens);
2752 }, debounce_timeout.relaxed);
2753
2754 const contentInputId = `world_entry_content_${entry.uid}`;
2755 const contentInput = template.find('textarea[name="content"]');
2756 contentInput.data('uid', entry.uid);
2757 contentInput.attr('id', contentInputId);
2758 contentInput.on('input', async function (_, { skipCount } = {}) {
2759 const uid = $(this).data('uid');
2760 const value = $(this).val();
2761 data.entries[uid].content = value;
2762
2763 setWIOriginalDataValue(data, uid, 'content', data.entries[uid].content);
2764 await saveWorldInfo(name, data);
2765
2766 if (skipCount) {
2767 return;
2768 }
2769
2770 // count tokens
2771 countTokensDebounced(counter, value);
2772 });
2773 contentInput.val(entry.content).trigger('input', { skipCount: true });
2774
2775 const contentExpandButton = template.find('.editor_maximize');
2776 contentExpandButton.attr('data-for', contentInputId);
2777
2778 template.find('.inline-drawer-toggle').on('click', function () {
2779 if (counter.data('first-run')) {
2780 counter.data('first-run', false);
2781 countTokensDebounced(counter, contentInput.val());
2782 if (!keyInput.isFancy) initScrollHeight(keyInput.control);
2783 if (!keySecondaryInput.isFancy) initScrollHeight(keySecondaryInput.control);
2784 }
2785 });
2786
2787 // selective
2788 const selectiveInput = template.find('input[name="selective"]');
2789 selectiveInput.data('uid', entry.uid);
2790 selectiveInput.on('input', async function () {
2791 const uid = $(this).data('uid');
2792 const value = $(this).prop('checked');
2793 data.entries[uid].selective = value;
2794
2795 setWIOriginalDataValue(data, uid, 'selective', data.entries[uid].selective);
2796 await saveWorldInfo(name, data);
2797
2798 const keysecondary = $(this)
2799 .closest('.world_entry')
2800 .find('.keysecondary');
2801
2802 const keysecondarytextpole = $(this)
2803 .closest('.world_entry')
2804 .find('.keysecondarytextpole');
2805
2806 const keyprimaryselect = $(this)
2807 .closest('.world_entry')
2808 .find('.keyprimaryselect');
2809
2810 const keyprimaryHeight = keyprimaryselect.outerHeight();
2811 keysecondarytextpole.css('height', keyprimaryHeight + 'px');
2812
2813 value ? keysecondary.show() : keysecondary.hide();
2814
2815 });
2816 //forced on, ignored if empty
2817 selectiveInput.prop('checked', true /* entry.selective */).trigger('input');
2818 selectiveInput.parent().hide();
2819
2820
2821 // constant
2822 /*
2823 const constantInput = template.find('input[name="constant"]');
2824 constantInput.data("uid", entry.uid);
2825 constantInput.on("input", async function () {
2826 const uid = $(this).data("uid");
2827 const value = $(this).prop("checked");
2828 data.entries[uid].constant = value;
2829 setOriginalDataValue(data, uid, "constant", data.entries[uid].constant);
2830 await saveWorldInfo(name, data);
2831 });
2832 constantInput.prop("checked", entry.constant).trigger("input");
2833 */
2834
2835 // order
2836 const orderInput = template.find('input[name="order"]');
2837 orderInput.data('uid', entry.uid);
2838 orderInput.on('input', async function () {
2839 const uid = $(this).data('uid');
2840 const value = Number($(this).val());
2841
2842 data.entries[uid].order = !isNaN(value) ? value : 0;
2843 updatePosOrdDisplay(uid);
2844 setWIOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order);
2845 await saveWorldInfo(name, data);
2846 });
2847 orderInput.val(entry.order).trigger('input');
2848 orderInput.css('width', 'calc(3em + 15px)');
2849
2850 // group
2851 const groupInput = template.find('input[name="group"]');
2852 groupInput.data('uid', entry.uid);
2853 groupInput.on('input', async function () {
2854 const uid = $(this).data('uid');
2855 const value = String($(this).val()).trim();
2856
2857 data.entries[uid].group = value;
2858 setWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group);
2859 await saveWorldInfo(name, data);
2860 });
2861 groupInput.val(entry.group ?? '').trigger('input');
2862 setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1);
2863
2864 // inclusion priority
2865 const groupOverrideInput = template.find('input[name="groupOverride"]');
2866 groupOverrideInput.data('uid', entry.uid);
2867 groupOverrideInput.on('input', async function () {
2868 const uid = $(this).data('uid');
2869 const value = $(this).prop('checked');
2870 data.entries[uid].groupOverride = value;
2871 setWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride);
2872 await saveWorldInfo(name, data);
2873 });
2874 groupOverrideInput.prop('checked', entry.groupOverride).trigger('input');
2875
2876 // group weight
2877 const groupWeightInput = template.find('input[name="groupWeight"]');
2878 groupWeightInput.data('uid', entry.uid);
2879 groupWeightInput.on('input', async function () {
2880 const uid = $(this).data('uid');
2881 let value = Number($(this).val());
2882 const min = Number($(this).attr('min'));
2883 const max = Number($(this).attr('max'));
2884
2885 // Clamp the value
2886 if (value < min) {
2887 value = min;
2888 $(this).val(min);
2889 } else if (value > max) {
2890 value = max;
2891 $(this).val(max);
2892 }
2893
2894 data.entries[uid].groupWeight = !isNaN(value) ? Math.abs(value) : 1;
2895 setWIOriginalDataValue(data, uid, 'extensions.group_weight', data.entries[uid].groupWeight);
2896 await saveWorldInfo(name, data);
2897 });
2898 groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input');
2899
2900 // sticky
2901 const sticky = template.find('input[name="sticky"]');
2902 sticky.data('uid', entry.uid);
2903 sticky.on('input', async function () {
2904 const uid = $(this).data('uid');
2905 const value = Number($(this).val());
2906 data.entries[uid].sticky = !isNaN(value) ? value : null;
2907
2908 setWIOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky);
2909 await saveWorldInfo(name, data);
2910 });
2911 sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input');
2912
2913 // cooldown
2914 const cooldown = template.find('input[name="cooldown"]');
2915 cooldown.data('uid', entry.uid);
2916 cooldown.on('input', async function () {
2917 const uid = $(this).data('uid');
2918 const value = Number($(this).val());
2919 data.entries[uid].cooldown = !isNaN(value) ? value : null;
2920
2921 setWIOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown);
2922 await saveWorldInfo(name, data);
2923 });
2924 cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input');
2925
2926 // delay
2927 const delay = template.find('input[name="delay"]');
2928 delay.data('uid', entry.uid);
2929 delay.on('input', async function () {
2930 const uid = $(this).data('uid');
2931 const value = Number($(this).val());
2932 data.entries[uid].delay = !isNaN(value) ? value : null;
2933
2934 setWIOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay);
2935 await saveWorldInfo(name, data);
2936 });
2937 delay.val(entry.delay > 0 ? entry.delay : '').trigger('input');
2938
2939 // probability
2940 if (entry.probability === undefined) {
2941 entry.probability = null;
2942 }
2943
2944 // depth
2945 const depthInput = template.find('input[name="depth"]');
2946 depthInput.data('uid', entry.uid);
2947
2948 depthInput.on('input', async function () {
2949 const uid = $(this).data('uid');
2950 const value = Number($(this).val());
2951
2952 data.entries[uid].depth = !isNaN(value) ? value : 0;
2953 updatePosOrdDisplay(uid);
2954 setWIOriginalDataValue(data, uid, 'extensions.depth', data.entries[uid].depth);
2955 await saveWorldInfo(name, data);
2956 });
2957 depthInput.val(entry.depth ?? DEFAULT_DEPTH).trigger('input');
2958 depthInput.css('width', 'calc(3em + 15px)');
2959
2960 // Hide by default unless depth is specified
2961 if (entry.position === world_info_position.atDepth) {
2962 //depthInput.parent().hide();
2963 }
2964
2965 const probabilityInput = template.find('input[name="probability"]');
2966 probabilityInput.data('uid', entry.uid);2735 probabilityInput.data('uid', entry.uid);
2967 probabilityInput.on('input', async function () {2736 probabilityInput.on('input', async function () {
2968 const uid = $(this).data('uid');2737 const uid = $(this).data('uid');
2969 const value = Number($(this).val());2738 const value = Number($(this).val());
2970
2971 data.entries[uid].probability = !isNaN(value) ? value : null;2739 data.entries[uid].probability = !isNaN(value) ? value : null;
2972
2973 // Clamp probability to 0-100
2974 if (data.entries[uid].probability !== null) {2740 if (data.entries[uid].probability !== null) {
2975 data.entries[uid].probability = Math.min(100, Math.max(0, data.entries[uid].probability));2741 data.entries[uid].probability = Math.min(100, Math.max(0, data.entries[uid].probability));
2976
2977 if (data.entries[uid].probability !== value) {2742 if (data.entries[uid].probability !== value) {
2978 $(this).val(data.entries[uid].probability);2743 $(this).val(data.entries[uid].probability);
2979 }2744 }
2980 }2745 }
2981
2982 setWIOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability);2746 setWIOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability);
2983 await saveWorldInfo(name, data);2747 await saveWorldInfo(name, data);
2984 });2748 });
2985 probabilityInput.val(entry.probability).trigger('input');2749 probabilityInput.val(entry.probability).trigger('input');
2986 probabilityInput.css('width', 'calc(3em + 15px)');2750 probabilityInput.css('width', 'calc(3em + 15px)');
2751}
29872752
2988 // probability toggle2753/**
2989 if (entry.useProbability === undefined) {2754 * Helper to handle probability toggle.
2990 entry.useProbability = false;2755 */
2991 }2756function handleProbabilityToggleHelper({ probabilityToggle, data, entry, name, probabilityInput, setWIOriginalDataValue, saveWorldInfo }) {
2992
2993 const probabilityToggle = template.find('input[name="useProbability"]');
2994 probabilityToggle.data('uid', entry.uid);2757 probabilityToggle.data('uid', entry.uid);
2995 probabilityToggle.on('input', async function () {2758 probabilityToggle.on('input', async function () {
2996 const uid = $(this).data('uid');2759 const uid = $(this).data('uid');
2997 const value = $(this).prop('checked');2760 const value = $(this).prop('checked');
2998 data.entries[uid].useProbability = value;2761 data.entries[uid].useProbability = value;
2999 const probabilityContainer = $(this)2762 const probabilityContainer = $(this).closest('.world_entry').find('.probabilityContainer');
3000 .closest('.world_entry')
3001 .find('.probabilityContainer');
3002 await saveWorldInfo(name, data);2763 await saveWorldInfo(name, data);
3003 value ? probabilityContainer.show() : probabilityContainer.hide();2764 value ? probabilityContainer.show() : probabilityContainer.hide();
3004
3005 if (value && data.entries[uid].probability === null) {2765 if (value && data.entries[uid].probability === null) {
3006 data.entries[uid].probability = 100;2766 data.entries[uid].probability = 100;
3007 }2767 }
3008
3009 if (!value) {2768 if (!value) {
3010 data.entries[uid].probability = null;2769 data.entries[uid].probability = null;
3011 }2770 }
3012
3013 probabilityInput.val(data.entries[uid].probability).trigger('input');2771 probabilityInput.val(data.entries[uid].probability).trigger('input');
3014 });2772 });
3015 //forced on, 100% by default2773 probabilityToggle.prop('checked', true).trigger('input');
3016 probabilityToggle.prop('checked', true /* entry.useProbability */).trigger('input');
3017 probabilityToggle.parent().hide();2774 probabilityToggle.parent().hide();
2775}
30182776
3019 // position2777/**
3020 if (entry.position === undefined) {2778 * Helper to handle select2 dropdowns for boolean selects.
3021 entry.position = 0;2779 */
3022 }2780function handleBooleanSelectHelper({ selectElem, entry, entryKey, data, name, setWIOriginalDataValue, saveWorldInfo }) {
30232781 selectElem.data('uid', entry.uid);
3024 const positionInput = template.find('select[name="position"]');2782 selectElem.on('input', async function () {
3025 //initScrollHeight(positionInput);2783 const uid = $(this).data('uid');
3026 positionInput.data('uid', entry.uid);2784 const value = $(this).val();
3027 positionInput.on('click', function (event) {2785 data.entries[uid][entryKey] = value === 'null' ? null : value === 'true';
3028 // Prevent closing the drawer on clicking the input2786 setWIOriginalDataValue(data, uid, `extensions.${entryKey.replace(/[A-Z]/g, m => `_${m.toLowerCase()}`)}`, data.entries[uid][entryKey]);
3029 event.stopPropagation();2787 await saveWorldInfo(name, data);
3030 });2788 });
3031 positionInput.on('input', async function () {2789 selectElem.val((entry[entryKey] === null || entry[entryKey] === undefined) ? 'null' : entry[entryKey] ? 'true' : 'false').trigger('input');
2790}
2791
2792/**
2793 * Helper to handle input fields for numbers.
2794 */
2795function handleNumberInputHelper({ inputElem, entry, entryKey, data, name, setWIOriginalDataValue, saveWorldInfo, min, max, clamp = false }) {
2796 inputElem.data('uid', entry.uid);
2797 inputElem.on('input', async function () {
3032 const uid = $(this).data('uid');2798 const uid = $(this).data('uid');
3033 const value = Number($(this).val());2799 let value = Number($(this).val());
3034 data.entries[uid].position = !isNaN(value) ? value : 0;2800 if (clamp) {
3035 if (value === world_info_position.atDepth) {2801 if (value < min) {
3036 depthInput.prop('disabled', false);2802 value = min;
3037 depthInput.css('visibility', 'visible');2803 $(this).val(min);
3038 //depthInput.parent().show();2804 } else if (value > max) {
3039 const role = Number($(this).find(':selected').data('role'));2805 value = max;
3040 data.entries[uid].role = role;2806 $(this).val(max);
3041 } else {2807 }
3042 depthInput.prop('disabled', true);
3043 depthInput.css('visibility', 'hidden');
3044 data.entries[uid].role = null;
3045 //depthInput.parent().hide();
3046 }2808 }
3047 updatePosOrdDisplay(uid);2809 data.entries[uid][entryKey] = !isNaN(value) ? value : null;
3048 // Spec v2 only supports before_char and after_char2810 setWIOriginalDataValue(data, uid, `extensions.${entryKey.replace(/[A-Z]/g, m => `_${m.toLowerCase()}`)}`, data.entries[uid][entryKey]);
3049 setWIOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char');
3050 // Write the original value as extensions field
3051 setWIOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position);
3052 setWIOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role);
3053 await saveWorldInfo(name, data);2811 await saveWorldInfo(name, data);
3054 });2812 });
2813 inputElem.val(entry[entryKey] ?? (clamp ? min : '')).trigger('input');
2814}
30552815
3056 const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : '';2816/**
3057 template2817 * Helper to handle tri-state selector for constant/normal/vectorized.
3058 .find(`select[name="position"] option[value="${entry.position}"][data-role="${roleValue}"]`)2818 */
3059 .prop('selected', true)2819function handleEntryStateSelectorHelper({ entryStateSelector, entry, data, name, setWIOriginalDataValue, saveWorldInfo }) {
3060 .trigger('input');
3061
3062 //add UID above content box (less important doesn't need to be always visible)
3063 template.find('.world_entry_form_uid_value').text(`(UID: ${entry.uid})`);
3064
3065 //new tri-state selector for constant/normal/vectorized
3066 const entryStateSelector = template.find('select[name="entryStateSelector"]');
3067 entryStateSelector.data('uid', entry.uid);2820 entryStateSelector.data('uid', entry.uid);
3068 entryStateSelector.on('click', function (event) {2821 entryStateSelector.on('click', function (event) {
3069 // Prevent closing the drawer on clicking the input
3070 event.stopPropagation();2822 event.stopPropagation();
3071 });2823 });
3072 entryStateSelector.on('input', async function () {2824 entryStateSelector.on('input', async function () {
@@ -3093,12 +2845,17 @@ export async function getWorldEntry(name, data, entry) {
3093 break;2845 break;
3094 }2846 }
3095 await saveWorldInfo(name, data);2847 await saveWorldInfo(name, data);
3096
3097 });2848 });
2849 const entryState = () => entry.constant === true ? 'constant' : entry.vectorized === true ? 'vectorized' : 'normal';
2850 entryStateSelector.find(`option[value=${entryState()}]`).prop('selected', true).trigger('input');
2851}
30982852
3099 const entryKillSwitch = template.find('div[name="entryKillSwitch"]');2853/**
2854 * Helper to handle kill switch toggle.
2855 */
2856function handleEntryKillSwitchHelper({ entryKillSwitch, entry, data, name, setWIOriginalDataValue, saveWorldInfo, template }) {
3100 entryKillSwitch.data('uid', entry.uid);2857 entryKillSwitch.data('uid', entry.uid);
3101 entryKillSwitch.on('click', async function (event) {2858 entryKillSwitch.on('click', async function () {
3102 const uid = entry.uid;2859 const uid = entry.uid;
3103 data.entries[uid].disable = !data.entries[uid].disable;2860 data.entries[uid].disable = !data.entries[uid].disable;
3104 const isActive = !data.entries[uid].disable;2861 const isActive = !data.entries[uid].disable;
@@ -3107,104 +2864,114 @@ export async function getWorldEntry(name, data, entry) {
3107 entryKillSwitch.toggleClass('fa-toggle-off', !isActive);2864 entryKillSwitch.toggleClass('fa-toggle-off', !isActive);
3108 entryKillSwitch.toggleClass('fa-toggle-on', isActive);2865 entryKillSwitch.toggleClass('fa-toggle-on', isActive);
3109 await saveWorldInfo(name, data);2866 await saveWorldInfo(name, data);
3110
3111 });2867 });
3112
3113 const entryState = function () {
3114 if (entry.constant === true) {
3115 return 'constant';
3116 } else if (entry.vectorized === true) {
3117 return 'vectorized';
3118 } else {
3119 return 'normal';
3120 }
3121 };
3122
3123 const isActive = !entry.disable;2868 const isActive = !entry.disable;
3124 template.toggleClass('disabledWIEntry', !isActive);2869 template.toggleClass('disabledWIEntry', !isActive);
3125 entryKillSwitch.toggleClass('fa-toggle-off', !isActive);2870 entryKillSwitch.toggleClass('fa-toggle-off', !isActive);
3126 entryKillSwitch.toggleClass('fa-toggle-on', isActive);2871 entryKillSwitch.toggleClass('fa-toggle-on', isActive);
2872}
2873
2874/**
2875 * Main function to build the WI entry editor template.
2876 */
2877export async function getWorldEntry(name, data, entry) {
2878 if (!data.entries[entry.uid]) return;
31272879
3128 template2880 const headerTemplate = WI_ENTRY_HEADER_TEMPLATE.clone();
3129 .find(`select[name="entryStateSelector"] option[value=${entryState()}]`)2881 headerTemplate.data('uid', entry.uid);
3130 .prop('selected', true)2882 headerTemplate.attr('uid', entry.uid);
3131 .trigger('input');
31322883
3133 // exclude recursion2884 if (typeof power_user.wi_key_input_plaintext === 'undefined') power_user.wi_key_input_plaintext = true;
3134 const excludeRecursionInput = template.find('input[name="exclude_recursion"]');2885
3135 excludeRecursionInput.data('uid', entry.uid);2886 // Comment
3136 excludeRecursionInput.on('input', async function () {2887 const commentInput = headerTemplate.find('textarea[name="comment"]');
2888 commentInput.data('uid', entry.uid);
2889 commentInput.on('input', async function (_, { skipReset } = {}) {
3137 const uid = $(this).data('uid');2890 const uid = $(this).data('uid');
3138 const value = $(this).prop('checked');2891 const value = $(this).val();
3139 data.entries[uid].excludeRecursion = value;2892 !skipReset && await resetScrollHeight(this);
3140 setWIOriginalDataValue(data, uid, 'extensions.exclude_recursion', data.entries[uid].excludeRecursion);2893 data.entries[uid].comment = value;
2894 setWIOriginalDataValue(data, uid, 'comment', data.entries[uid].comment);
3141 await saveWorldInfo(name, data);2895 await saveWorldInfo(name, data);
3142 });2896 });
3143 excludeRecursionInput.prop('checked', entry.excludeRecursion).trigger('input');2897 commentInput.val(entry.comment).trigger('input', { skipReset: true });
31442898
3145 // prevent recursion2899 // Order
3146 const preventRecursionInput = template.find('input[name="prevent_recursion"]');2900 const orderInput = headerTemplate.find('input[name="order"]');
3147 preventRecursionInput.data('uid', entry.uid);2901 orderInput.data('uid', entry.uid);
3148 preventRecursionInput.on('input', async function () {2902 orderInput.on('input', async function () {
3149 const uid = $(this).data('uid');2903 const uid = $(this).data('uid');
3150 const value = $(this).prop('checked');2904 const value = Number($(this).val());
3151 data.entries[uid].preventRecursion = value;2905 data.entries[uid].order = !isNaN(value) ? value : 0;
3152 setWIOriginalDataValue(data, uid, 'extensions.prevent_recursion', data.entries[uid].preventRecursion);2906 updatePosOrdDisplayHelper({ template: headerTemplate, data, uid });
2907 setWIOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order);
3153 await saveWorldInfo(name, data);2908 await saveWorldInfo(name, data);
3154 });2909 });
3155 preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input');2910 orderInput.val(entry.order).trigger('input');
31562911 orderInput.css('width', 'calc(3em + 15px)');
3157 // delay until recursion
3158 // delay until recursion level
3159 const delayUntilRecursionInput = template.find('input[name="delay_until_recursion"]');
3160 delayUntilRecursionInput.data('uid', entry.uid);
3161 const delayUntilRecursionLevelInput = template.find('input[name="delayUntilRecursionLevel"]');
3162 delayUntilRecursionLevelInput.data('uid', entry.uid);
3163 delayUntilRecursionInput.on('input', async function () {
3164 const uid = $(this).data('uid');
3165 const toggled = $(this).prop('checked');
3166
3167 // If the value contains a number, we'll take that one (set by the level input), otherwise we can use true/false switch
3168 const value = toggled ? data.entries[uid].delayUntilRecursion || true : false;
31692912
3170 if (!toggled) delayUntilRecursionLevelInput.val('');2913 // Probability
2914 handleProbabilityInputHelper({ probabilityInput: headerTemplate.find('input[name="probability"]'), data, entry, name, setWIOriginalDataValue, saveWorldInfo });
31712915
3172 data.entries[uid].delayUntilRecursion = value;2916 // Depth
3173 setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);2917 handleNumberInputHelper({
3174 await saveWorldInfo(name, data);2918 inputElem: headerTemplate.find('input[name="depth"]'),
2919 entry, entryKey: 'depth', data, name, setWIOriginalDataValue, saveWorldInfo,
2920 min: 0, max: MAX_SCAN_DEPTH, clamp: false,
3175 });2921 });
3176 delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');2922 headerTemplate.find('input[name="depth"]').css('width', 'calc(3em + 15px)');
3177 delayUntilRecursionLevelInput.on('input', async function () {2923
2924 // Position
2925 if (entry.position === undefined) entry.position = 0;
2926 const positionInput = headerTemplate.find('select[name="position"]');
2927 positionInput.data('uid', entry.uid);
2928 positionInput.on('click', e => e.stopPropagation());
2929 positionInput.on('input', async function () {
3178 const uid = $(this).data('uid');2930 const uid = $(this).data('uid');
3179 const content = $(this).val();2931 const value = Number($(this).val());
3180 const value = content === '' ? (typeof data.entries[uid].delayUntilRecursion === 'boolean' ? data.entries[uid].delayUntilRecursion : true)2932 data.entries[uid].position = !isNaN(value) ? value : 0;
3181 : content === 1 ? true2933 const depthInput = headerTemplate.find('input[name="depth"]');
3182 : !isNaN(Number(content)) ? Number(content)2934 if (value === world_info_position.atDepth) {
3183 : false;2935 depthInput.prop('disabled', false);
31842936 depthInput.css('visibility', 'visible');
3185 data.entries[uid].delayUntilRecursion = value;2937 const role = Number($(this).find(':selected').data('role'));
3186 setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);2938 data.entries[uid].role = role;
2939 } else {
2940 depthInput.prop('disabled', true);
2941 depthInput.css('visibility', 'hidden');
2942 data.entries[uid].role = null;
2943 }
2944 updatePosOrdDisplayHelper({ template: headerTemplate, data, uid });
2945 setWIOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char');
2946 setWIOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position);
2947 setWIOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role);
3187 await saveWorldInfo(name, data);2948 await saveWorldInfo(name, data);
3188 });2949 });
3189 // No need to retrigger inpout event, we'll just set the curret current value. It was edited/saved above already2950 const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : '';
3190 delayUntilRecursionLevelInput.val(['number', 'string'].includes(typeof entry.delayUntilRecursion) ? entry.delayUntilRecursion : '').trigger('input');2951 headerTemplate.find(`select[name="position"] option[value="${entry.position}"][data-role="${roleValue}"]`).prop('selected', true).trigger('input');
2952
2953 // Tri-state selector
2954 handleEntryStateSelectorHelper({
2955 entryStateSelector: headerTemplate.find('select[name="entryStateSelector"]'),
2956 entry, data, name, setWIOriginalDataValue, saveWorldInfo,
2957 });
2958
2959 // Kill switch
2960 handleEntryKillSwitchHelper({
2961 entryKillSwitch: headerTemplate.find('div[name="entryKillSwitch"]'),
2962 entry, data, name, setWIOriginalDataValue, saveWorldInfo, template: headerTemplate,
2963 });
31912964
3192 // duplicate button2965 // Duplicate/delete/move buttons
3193 const duplicateButton = template.find('.duplicate_entry_button');2966 headerTemplate.find('.duplicate_entry_button').data('uid', entry.uid).on('click', async function () {
3194 duplicateButton.data('uid', entry.uid);
3195 duplicateButton.on('click', async function () {
3196 const uid = $(this).data('uid');2967 const uid = $(this).data('uid');
3197 const entry = duplicateWorldInfoEntry(data, uid);2968 const entryDup = duplicateWorldInfoEntry(data, uid);
3198 if (entry) {2969 if (entryDup) {
3199 await saveWorldInfo(name, data);2970 await saveWorldInfo(name, data);
3200 updateEditor(entry.uid);2971 updateEditor(entryDup.uid);
3201 }2972 }
3202 });2973 });
32032974 headerTemplate.find('.delete_entry_button').data('uid', entry.uid).on('click', async function (e) {
3204 // delete button
3205 const deleteButton = template.find('.delete_entry_button');
3206 deleteButton.data('uid', entry.uid);
3207 deleteButton.on('click', async function (e) {
3208 e.stopPropagation();2975 e.stopPropagation();
3209 const uid = $(this).data('uid');2976 const uid = $(this).data('uid');
3210 const deleted = await deleteWorldInfoEntry(data, uid);2977 const deleted = await deleteWorldInfoEntry(data, uid);
@@ -3213,36 +2980,24 @@ export async function getWorldEntry(name, data, entry) {
3213 await saveWorldInfo(name, data);2980 await saveWorldInfo(name, data);
3214 updateEditor(navigation_option.previous);2981 updateEditor(navigation_option.previous);
3215 });2982 });
32162983 headerTemplate.find('.move_entry_button').attr('data-uid', entry.uid).attr('data-current-world', name).on('click', async function (e) {
3217 // move button
3218 const moveButton = template.find('.move_entry_button');
3219 moveButton.attr('data-uid', entry.uid);
3220 moveButton.attr('data-current-world', name);
3221 moveButton.on('click', async function (e) {
3222 e.stopPropagation();2984 e.stopPropagation();
3223 const sourceUid = $(this).attr('data-uid');2985 const sourceUid = $(this).attr('data-uid');
3224 const sourceWorld = $(this).attr('data-current-world');2986 const sourceWorld = $(this).attr('data-current-world');
3225 const sourceWorldInfo = await loadWorldInfo(sourceWorld);2987 const sourceWorldInfo = await loadWorldInfo(sourceWorld);
3226 if (!sourceWorldInfo) {2988 if (!sourceWorldInfo) return;
3227 return;
3228 }
3229 const sourceName = sourceWorldInfo.entries[sourceUid]?.comment;2989 const sourceName = sourceWorldInfo.entries[sourceUid]?.comment;
3230 if (sourceName === undefined) {2990 if (sourceName === undefined) return;
3231 return;
3232 }
3233
3234 const select = document.createElement('select');2991 const select = document.createElement('select');
3235 select.id = 'move_entry_target_select';2992 select.id = 'move_entry_target_select';
3236 select.classList.add('text_pole', 'wide100p', 'marginTop10');2993 select.classList.add('text_pole', 'wide100p', 'marginTop10');
3237
3238 const defaultOption = document.createElement('option');2994 const defaultOption = document.createElement('option');
3239 defaultOption.value = '';2995 defaultOption.value = '';
3240 defaultOption.textContent = `-- ${t`Select Target Lorebook`} --`;2996 defaultOption.textContent = `-- ${t`Select Target Lorebook`} --`;
3241 select.appendChild(defaultOption);2997 select.appendChild(defaultOption);
3242
3243 let selectableWorldCount = 0;2998 let selectableWorldCount = 0;
3244 world_names.forEach(worldName => {2999 world_names.forEach(worldName => {
3245 if (worldName !== sourceWorld) { // Exclude current world3000 if (worldName !== sourceWorld) {
3246 const option = document.createElement('option');3001 const option = document.createElement('option');
3247 option.value = world_names.indexOf(worldName).toString();3002 option.value = world_names.indexOf(worldName).toString();
3248 option.textContent = worldName;3003 option.textContent = worldName;
@@ -3250,177 +3005,343 @@ export async function getWorldEntry(name, data, entry) {
3250 selectableWorldCount++;3005 selectableWorldCount++;
3251 }3006 }
3252 });3007 });
3253
3254 if (selectableWorldCount === 0) {3008 if (selectableWorldCount === 0) {
3255 toastr.warning(t`There are no other lorebooks to move to.`);3009 toastr.warning(t`There are no other lorebooks to move to.`);
3256 return;3010 return;
3257 }3011 }
3258
3259 // Create wrapper div
3260 const wrapper = document.createElement('div');3012 const wrapper = document.createElement('div');
3261 wrapper.textContent = t`Move '${sourceName}' to:`;3013 wrapper.textContent = t`Move '${sourceName}' to:`;
3262
3263 // Create container and append elements
3264 const container = document.createElement('div');3014 const container = document.createElement('div');
3265 container.appendChild(wrapper);3015 container.appendChild(wrapper);
3266 container.appendChild(select);3016 container.appendChild(select);
3267
3268 let selectedWorldIndex = -1;3017 let selectedWorldIndex = -1;
3269 select.addEventListener('change', function() {3018 select.addEventListener('change', function () {
3270 selectedWorldIndex = this.value === '' ? -1 : Number(this.value);3019 selectedWorldIndex = this.value === '' ? -1 : Number(this.value);
3271 });3020 });
3272
3273 const popupConfirm = await callGenericPopup(container, POPUP_TYPE.CONFIRM, '', {3021 const popupConfirm = await callGenericPopup(container, POPUP_TYPE.CONFIRM, '', {
3274 okButton: t`Move`,3022 okButton: t`Move`,
3275 cancelButton: t`Cancel`,3023 cancelButton: t`Cancel`,
3276 });3024 });
3277 if (!popupConfirm) {3025 if (!popupConfirm) return;
3278 return;3026 if (selectedWorldIndex === -1) return;
3279 }
3280
3281 if (selectedWorldIndex === -1) {
3282 return;
3283 }
3284
3285 const selectedValue = world_names[selectedWorldIndex];3027 const selectedValue = world_names[selectedWorldIndex];
3286
3287 if (!selectedValue) {3028 if (!selectedValue) {
3288 toastr.warning(t`Please select a target lorebook.`);3029 toastr.warning(t`Please select a target lorebook.`);
3289 return;3030 return;
3290 }3031 }
3291
3292 await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid);3032 await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid);
3293 });3033 });
32943034
3295 // scan depth3035 let drawerInitialized = false;
3296 const scanDepthInput = template.find('input[name="scanDepth"]');3036 let drawerDestroyTimeout = null;
3297 scanDepthInput.data('uid', entry.uid);3037 headerTemplate.find('.inline-drawer').on('inline-drawer-toggle', function () {
3298 scanDepthInput.on('input', async function () {3038 if (drawerDestroyTimeout) {
3299 const uid = $(this).data('uid');3039 clearTimeout(drawerDestroyTimeout);
3300 const isEmpty = $(this).val() === '';3040 drawerDestroyTimeout = null;
3301 const value = Number($(this).val());3041 }
33023042 if (drawerInitialized) {
3303 // Clamp if necessary3043 drawerDestroyTimeout = setTimeout(() => {
3304 if (value < 0) {3044 // Drawer was reopened, so we don't destroy it
3305 $(this).val(0).trigger('input');3045 if (editOutlet.is(':visible')) {
3306 toastr.warning('Scan depth cannot be negative');3046 return;
3307 return;3047 }
3308 }3048 drawerInitialized = false;
33093049 clearEntryList(editOutlet);
3310 if (value > MAX_SCAN_DEPTH) {3050 drawerDestroyTimeout = null;
3311 $(this).val(MAX_SCAN_DEPTH).trigger('input');3051 }, debounce_timeout.relaxed);
3312 toastr.warning(`Scan depth cannot exceed ${MAX_SCAN_DEPTH}`);3052 } else {
3313 return;3053 drawerInitialized = true;
3054 addEditorDrawerContent();
3314 }3055 }
3315
3316 data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null;
3317 setWIOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth);
3318 await saveWorldInfo(name, data);
3319 });3056 });
3320 scanDepthInput.val(entry.scanDepth ?? null).trigger('input');
33213057
3322 // case sensitive select3058 const editOutlet = headerTemplate.find('.inline-drawer-outlet');
3323 const caseSensitiveSelect = template.find('select[name="caseSensitive"]');
3324 caseSensitiveSelect.data('uid', entry.uid);
3325 caseSensitiveSelect.on('input', async function () {
3326 const uid = $(this).data('uid');
3327 const value = $(this).val();
33283059
3329 data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true';3060 function addEditorDrawerContent() {
3330 setWIOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive);3061 const editTemplate = WI_ENTRY_EDIT_TEMPLATE.clone();
3331 await saveWorldInfo(name, data);
3332 });
3333 caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input');
33343062
3335 // match whole words select3063 // UID display
3336 const matchWholeWordsSelect = template.find('select[name="matchWholeWords"]');3064 editTemplate.find('.world_entry_form_uid_value').text(`(UID: ${entry.uid})`);
3337 matchWholeWordsSelect.data('uid', entry.uid);
3338 matchWholeWordsSelect.on('input', async function () {
3339 const uid = $(this).data('uid');
3340 const value = $(this).val();
33413065
3342 data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true';3066 // Key inputs
3343 setWIOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords);3067 const keyInput = enableKeysInputHelper({ template: editTemplate, entry, entryPropName: 'key', originalDataValueName: 'keys', name, data });
3344 await saveWorldInfo(name, data);3068 const keySecondaryInput = enableKeysInputHelper({ template: editTemplate, entry, entryPropName: 'keysecondary', originalDataValueName: 'secondary_keys', name, data });
3345 });3069 if (!keyInput.isFancy) initScrollHeight(keyInput.control);
3346 matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input');3070 if (!keySecondaryInput.isFancy) initScrollHeight(keySecondaryInput.control);
33473071
3348 // use group scoring select3072 // Key input switch
3349 const useGroupScoringSelect = template.find('select[name="useGroupScoring"]');3073 editTemplate.find('.switch_input_type_icon').on('click', function () {
3350 useGroupScoringSelect.data('uid', entry.uid);3074 power_user.wi_key_input_plaintext = !power_user.wi_key_input_plaintext;
3351 useGroupScoringSelect.on('input', async function () {3075 saveSettingsDebounced();
3352 const uid = $(this).data('uid');3076 const uid = ($(this).parents('.world_entry')).data('uid');
3353 const value = $(this).val();3077 updateEditor(uid, false);
3078 $(`.world_entry[uid="${uid}"] .inline-drawer-icon`).trigger('click');
3079 }).each((_, icon) => {
3080 $(icon).attr('title', $(icon).data(power_user.wi_key_input_plaintext ? 'tooltip-on' : 'tooltip-off'));
3081 $(icon).text($(icon).data(power_user.wi_key_input_plaintext ? 'icon-on' : 'icon-off'));
3082 });
33543083
3355 data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true';3084 // Probability toggle
3356 setWIOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring);3085 handleProbabilityToggleHelper({
3357 await saveWorldInfo(name, data);3086 probabilityToggle: editTemplate.find('input[name="useProbability"]'),
3358 });3087 data, entry, name,
3359 useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input');3088 probabilityInput: headerTemplate.find('input[name="probability"]'),
3089 setWIOriginalDataValue, saveWorldInfo,
3090 });
33603091
3361 function handleMatchCheckbox(fieldName) {3092 // Comment toggle
3362 const key = originalWIDataKeyMap[fieldName];3093 const commentToggle = editTemplate.find('input[name="addMemo"]');
3363 const checkBoxElem = template.find(`input[type="checkbox"][name="${fieldName}"]`);3094 commentToggle.data('uid', entry.uid);
3364 checkBoxElem.data('uid', entry.uid);3095 commentToggle.on('input', async function () {
3365 checkBoxElem.on('input', async function () {
3366 const uid = $(this).data('uid');3096 const uid = $(this).data('uid');
3367 const value = $(this).prop('checked');3097 const value = $(this).prop('checked');
3098 const commentContainer = $(this).closest('.world_entry').find('.commentContainer');
3099 data.entries[uid].addMemo = value;
3100 await saveWorldInfo(name, data);
3101 value ? commentContainer.show() : commentContainer.hide();
3102 });
3103 commentToggle.prop('checked', true).trigger('input');
3104 commentToggle.parent().hide();
3105
3106 // Logic AND/NOT
3107 const selectiveLogicDropdown = editTemplate.find('select[name="entryLogicType"]');
3108 selectiveLogicDropdown.data('uid', entry.uid);
3109 selectiveLogicDropdown.on('click', e => e.stopPropagation());
3110 selectiveLogicDropdown.on('input', async function () {
3111 const uid = $(this).data('uid');
3112 const value = Number($(this).val());
3113 data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY;
3114 setWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic);
3115 await saveWorldInfo(name, data);
3116 });
3117 editTemplate.find(`select[name="entryLogicType"] option[value=${entry.selectiveLogic}]`).prop('selected', true).trigger('input');
33683118
3369 data.entries[uid][fieldName] = value;3119 // Selective
3370 setWIOriginalDataValue(data, uid, key, data.entries[uid][fieldName]);3120 const selectiveInput = editTemplate.find('input[name="selective"]');
3121 selectiveInput.data('uid', entry.uid);
3122 selectiveInput.on('input', async function () {
3123 const uid = $(this).data('uid');
3124 const value = $(this).prop('checked');
3125 data.entries[uid].selective = value;
3126 setWIOriginalDataValue(data, uid, 'selective', data.entries[uid].selective);
3371 await saveWorldInfo(name, data);3127 await saveWorldInfo(name, data);
3128 const keysecondary = $(this).closest('.world_entry').find('.keysecondary');
3129 const keysecondarytextpole = $(this).closest('.world_entry').find('.keysecondarytextpole');
3130 const keyprimaryselect = $(this).closest('.world_entry').find('.keyprimaryselect');
3131 const keyprimaryHeight = keyprimaryselect.outerHeight();
3132 keysecondarytextpole.css('height', keyprimaryHeight + 'px');
3133 value ? keysecondary.show() : keysecondary.hide();
3134 });
3135 selectiveInput.prop('checked', true).trigger('input');
3136 selectiveInput.parent().hide();
3137
3138 // Character filter
3139 const characterFilterLabel = editTemplate.find('label[for="characterFilter"] > small');
3140 characterFilterLabel.text(entry.characterFilter?.isExclude ? 'Exclude Character(s)' : 'Filter to Character(s)');
3141 const characterExclusionInput = editTemplate.find('input[name="character_exclusion"]');
3142 characterExclusionInput.data('uid', entry.uid);
3143 characterExclusionInput.on('input', async function () {
3144 const uid = $(this).data('uid');
3145 const value = $(this).prop('checked');
3146 characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)');
3147 if (data.entries[uid].characterFilter) {
3148 if (!value && data.entries[uid].characterFilter.names.length === 0 && data.entries[uid].characterFilter.tags.length === 0) {
3149 delete data.entries[uid].characterFilter;
3150 } else {
3151 data.entries[uid].characterFilter.isExclude = value;
3152 }
3153 } else if (value) {
3154 Object.assign(data.entries[uid], { characterFilter: { isExclude: true, names: [], tags: [] } });
3155 }
3156 if (data.entries[uid]?.characterFilter?.names?.length > 0) {
3157 for (const name of [...data.entries[uid].characterFilter.names]) {
3158 if (!getContext().characters.find(x => x.avatar.replace(/\.[^/.]+$/, '') === name)) {
3159 data.entries[uid].characterFilter.names = data.entries[uid].characterFilter.names.filter(x => x !== name);
3160 }
3161 }
3162 }
3163 setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);
3164 await saveWorldInfo(name, data);
3165 });
3166 characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input');
3167
3168 const characterFilter = editTemplate.find('select[name="characterFilter"]');
3169 characterFilter.data('uid', entry.uid);
3170 initCharacterFilterSelect2Helper(characterFilter, t);
3171 fillCharacterAndTagOptionsHelper({ characterFilter, entry, getContext });
3172 handleCharacterFilterChangeHelper({
3173 characterFilter, data, entry, name, world_names, getContext, setWIOriginalDataValue, saveWorldInfo, t,
3372 });3174 });
3373 checkBoxElem.prop('checked', !!entry[fieldName]).trigger('input');
3374 }
33753175
3376 handleMatchCheckbox('matchPersonaDescription');3176 // Content
3377 handleMatchCheckbox('matchCharacterDescription');3177 const counter = editTemplate.find('.world_entry_form_token_counter');
3378 handleMatchCheckbox('matchCharacterPersonality');3178 const countTokensDebounced = debounce(async function (counter, value) {
3379 handleMatchCheckbox('matchCharacterDepthPrompt');3179 const numberOfTokens = await getTokenCountAsync(value);
3380 handleMatchCheckbox('matchScenario');3180 $(counter).text(numberOfTokens);
3381 handleMatchCheckbox('matchCreatorNotes');3181 }, debounce_timeout.relaxed);
3182 const contentInputId = `world_entry_content_${entry.uid}`;
3183 const contentInput = editTemplate.find('textarea[name="content"]');
3184 contentInput.data('uid', entry.uid);
3185 contentInput.attr('id', contentInputId);
3186 contentInput.on('input', async function (_, { skipCount } = {}) {
3187 const uid = $(this).data('uid');
3188 const value = $(this).val();
3189 data.entries[uid].content = value;
3190 setWIOriginalDataValue(data, uid, 'content', data.entries[uid].content);
3191 await saveWorldInfo(name, data);
3192 if (!skipCount) countTokensDebounced(counter, value);
3193 });
3194 contentInput.val(entry.content).trigger('input', { skipCount: true });
3195 editTemplate.find('.editor_maximize').attr('data-for', contentInputId);
33823196
3383 // automation id3197 // Scan depth
3384 const automationIdInput = template.find('input[name="automationId"]');3198 const scanDepthInput = editTemplate.find('input[name="scanDepth"]');
3385 automationIdInput.data('uid', entry.uid);3199 scanDepthInput.data('uid', entry.uid);
3386 automationIdInput.on('input', async function () {3200 scanDepthInput.on('input', async function () {
3387 const uid = $(this).data('uid');3201 const uid = $(this).data('uid');
3388 const value = $(this).val();3202 const isEmpty = $(this).val() === '';
3203 const value = Number($(this).val());
3204 if (value < 0) {
3205 $(this).val(0).trigger('input');
3206 toastr.warning('Scan depth cannot be negative');
3207 return;
3208 }
3209 if (value > MAX_SCAN_DEPTH) {
3210 $(this).val(MAX_SCAN_DEPTH).trigger('input');
3211 toastr.warning(`Scan depth cannot exceed ${MAX_SCAN_DEPTH}`);
3212 return;
3213 }
3214 data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null;
3215 setWIOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth);
3216 await saveWorldInfo(name, data);
3217 });
3218 scanDepthInput.val(entry.scanDepth ?? null).trigger('input');
33893219
3390 data.entries[uid].automationId = value;3220 // Group
3391 setWIOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId);3221 const groupInput = editTemplate.find('input[name="group"]');
3392 await saveWorldInfo(name, data);3222 groupInput.data('uid', entry.uid);
3393 });3223 groupInput.on('input', async function () {
3394 automationIdInput.val(entry.automationId ?? '').trigger('input');3224 const uid = $(this).data('uid');
3395 setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1);3225 const value = String($(this).val()).trim();
3226 data.entries[uid].group = value;
3227 setWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group);
3228 await saveWorldInfo(name, data);
3229 });
3230 groupInput.val(entry.group ?? '').trigger('input');
3231 setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1);
33963232
3397 template.find('.inline-drawer-content').css('display', 'none'); //entries start collapsed3233 // Inclusion priority
3234 const groupOverrideInput = editTemplate.find('input[name="groupOverride"]');
3235 groupOverrideInput.data('uid', entry.uid);
3236 groupOverrideInput.on('input', async function () {
3237 const uid = $(this).data('uid');
3238 const value = $(this).prop('checked');
3239 data.entries[uid].groupOverride = value;
3240 setWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride);
3241 await saveWorldInfo(name, data);
3242 });
3243 groupOverrideInput.prop('checked', entry.groupOverride).trigger('input');
33983244
3399 function updatePosOrdDisplay(uid) {3245 // Group weight
3400 // display position/order info left of keyword box3246 handleNumberInputHelper({
3401 let entry = data.entries[uid];3247 inputElem: editTemplate.find('input[name="groupWeight"]'),
3402 let posText = entry.position;3248 entry, entryKey: 'groupWeight', data, name, setWIOriginalDataValue, saveWorldInfo,
3403 switch (entry.position) {3249 min: 1, max: 10000, clamp: true,
3404 case 0:3250 });
3405 posText = '↑CD';3251
3406 break;3252 // Sticky, cooldown, delay
3407 case 1:3253 handleNumberInputHelper({
3408 posText = 'CD↓';3254 inputElem: editTemplate.find('input[name="sticky"]'),
3409 break;3255 entry, entryKey: 'sticky', data, name, setWIOriginalDataValue, saveWorldInfo,
3410 case 2:3256 min: 1, max: 10000, clamp: false,
3411 posText = '↑AN';3257 });
3412 break;3258 handleNumberInputHelper({
3413 case 3:3259 inputElem: editTemplate.find('input[name="cooldown"]'),
3414 posText = 'AN↓';3260 entry, entryKey: 'cooldown', data, name, setWIOriginalDataValue, saveWorldInfo,
3415 break;3261 min: 1, max: 10000, clamp: false,
3416 case 4:3262 });
3417 posText = `@D${entry.depth}`;3263 handleNumberInputHelper({
3418 break;3264 inputElem: editTemplate.find('input[name="delay"]'),
3419 }3265 entry, entryKey: 'delay', data, name, setWIOriginalDataValue, saveWorldInfo,
3420 template.find('.world_entry_form_position_value').text(`(${posText} ${entry.order})`);3266 min: 1, max: 10000, clamp: false,
3267 });
3268
3269 // Exclude/prevent recursion
3270 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'excludeRecursion', data, name });
3271 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'preventRecursion', data, name });
3272
3273 // Delay until recursion
3274 const delayUntilRecursionInput = editTemplate.find('input[name="delay_until_recursion"]');
3275 delayUntilRecursionInput.data('uid', entry.uid);
3276 const delayUntilRecursionLevelInput = editTemplate.find('input[name="delayUntilRecursionLevel"]');
3277 delayUntilRecursionLevelInput.data('uid', entry.uid);
3278 delayUntilRecursionInput.on('input', async function () {
3279 const uid = $(this).data('uid');
3280 const toggled = $(this).prop('checked');
3281 const value = toggled ? data.entries[uid].delayUntilRecursion || true : false;
3282 if (!toggled) delayUntilRecursionLevelInput.val('');
3283 data.entries[uid].delayUntilRecursion = value;
3284 setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
3285 await saveWorldInfo(name, data);
3286 });
3287 delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');
3288 delayUntilRecursionLevelInput.on('input', async function () {
3289 const uid = $(this).data('uid');
3290 const content = $(this).val();
3291 const value = content === '' ? (typeof data.entries[uid].delayUntilRecursion === 'boolean' ? data.entries[uid].delayUntilRecursion : true)
3292 : content === 1 ? true
3293 : !isNaN(Number(content)) ? Number(content)
3294 : false;
3295 data.entries[uid].delayUntilRecursion = value;
3296 setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);
3297 await saveWorldInfo(name, data);
3298 });
3299 delayUntilRecursionLevelInput.val(['number', 'string'].includes(typeof entry.delayUntilRecursion) ? entry.delayUntilRecursion : '').trigger('input');
3300
3301 // Boolean selects
3302 handleBooleanSelectHelper({
3303 selectElem: editTemplate.find('select[name="caseSensitive"]'),
3304 entry, entryKey: 'caseSensitive', data, name, setWIOriginalDataValue, saveWorldInfo,
3305 });
3306 handleBooleanSelectHelper({
3307 selectElem: editTemplate.find('select[name="matchWholeWords"]'),
3308 entry, entryKey: 'matchWholeWords', data, name, setWIOriginalDataValue, saveWorldInfo,
3309 });
3310 handleBooleanSelectHelper({
3311 selectElem: editTemplate.find('select[name="useGroupScoring"]'),
3312 entry, entryKey: 'useGroupScoring', data, name, setWIOriginalDataValue, saveWorldInfo,
3313 });
3314
3315 // Match checkboxes
3316 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchPersonaDescription', data, name });
3317 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterDescription', data, name });
3318 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterPersonality', data, name });
3319 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterDepthPrompt', data, name });
3320 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchScenario', data, name });
3321 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCreatorNotes', data, name });
3322
3323 // Automation ID
3324 const automationIdInput = editTemplate.find('input[name="automationId"]');
3325 automationIdInput.data('uid', entry.uid);
3326 automationIdInput.on('input', async function () {
3327 const uid = $(this).data('uid');
3328 const value = $(this).val();
3329 data.entries[uid].automationId = value;
3330 setWIOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId);
3331 await saveWorldInfo(name, data);
3332 });
3333 automationIdInput.val(entry.automationId ?? '').trigger('input');
3334 setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1);
3335
3336 countTokensDebounced(counter, contentInput.val());
3337
3338 editTemplate.find('.inline-drawer-content').css('display', 'none');
3339 editOutlet.append(editTemplate);
3421 }3340 }
34223341
3423 return template;3342 headerTemplate.find('.inline-drawer-content').css('display', 'none');
3343
3344 return headerTemplate;
3424}3345}
34253346
3426/**3347/**
@@ -3661,6 +3582,11 @@ async function _save(name, data) {
3661 * @return {Promise<void>} A promise that resolves when the world info is saved3582 * @return {Promise<void>} A promise that resolves when the world info is saved
3662 */3583 */
3663export async function saveWorldInfo(name, data, immediately = false) {3584export async function saveWorldInfo(name, data, immediately = false) {
3585 // Saving is temporarily disabled
3586 if (isSaveWorldInfoDisabled) {
3587 return;
3588 }
3589
3664 if (!name || !data) {3590 if (!name || !data) {
3665 return;3591 return;
3666 }3592 }
@@ -3819,7 +3745,7 @@ export async function createNewWorldInfo(worldName, { interactive = false } = {}
3819 if (selectedIndex !== -1) {3745 if (selectedIndex !== -1) {
3820 $('#world_editor_select').val(selectedIndex).trigger('change');3746 $('#world_editor_select').val(selectedIndex).trigger('change');
3821 } else {3747 } else {
3822 hideWorldEditor();3748 await hideWorldEditor();
3823 }3749 }
38243750
3825 return true;3751 return true;
@@ -4067,6 +3993,8 @@ function parseDecorators(content) {
4067 * @property {Set<any>} allActivatedEntries All entries.3993 * @property {Set<any>} allActivatedEntries All entries.
4068 * @returns {Promise<WIActivated>} The world info activated.3994 * @returns {Promise<WIActivated>} The world info activated.
4069 */3995 */
3996
3997//MARK: checkWorldInfo
4070export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData) {3998export async function checkWorldInfo(chat, maxContext, isDryRun, globalScanData) {
4071 const context = getContext();3999 const context = getContext();
4072 const buffer = new WorldInfoBuffer(chat, globalScanData);4000 const buffer = new WorldInfoBuffer(chat, globalScanData);
@@ -5385,7 +5313,7 @@ export function initWorldInfo() {
5385 const selectedIndex = String($('#world_editor_select').find(':selected').val());5313 const selectedIndex = String($('#world_editor_select').find(':selected').val());
53865314
5387 if (selectedIndex === '') {5315 if (selectedIndex === '') {
5388 hideWorldEditor();5316 await hideWorldEditor();
5389 } else {5317 } else {
5390 const worldName = world_names[selectedIndex];5318 const worldName = world_names[selectedIndex];
5391 showWorldEditor(worldName);5319 showWorldEditor(worldName);
@@ -5537,9 +5465,12 @@ export function initWorldInfo() {
5537 select2ChoiceClickSubscribe($('#world_info'), target => {5465 select2ChoiceClickSubscribe($('#world_info'), target => {
5538 const name = $(target).text();5466 const name = $(target).text();
5539 const selectedIndex = world_names.indexOf(name);5467 const selectedIndex = world_names.indexOf(name);
5540 if (selectedIndex !== -1) {5468 const alreadySelectedInEditor = $('#world_editor_select option:selected').text() === name;
5469 if (selectedIndex !== -1 && !alreadySelectedInEditor) {
5541 $('#world_editor_select').val(selectedIndex).trigger('change');5470 $('#world_editor_select').val(selectedIndex).trigger('change');
5542 console.log('Quick selection of world', name);5471 console.log('Quick selection of world', name);
5472 } else {
5473 console.warn('lets not reload an already loaded list yes?');
5543 }5474 }
5544 }, { buttonStyle: true, closeDrawer: true });5475 }, { buttonStyle: true, closeDrawer: true });
5545 }5476 }
public/style.css+133 -72
@@ -14,6 +14,7 @@
14@import url(css/data-maid.css);14@import url(css/data-maid.css);
1515
16:root {16:root {
17 interpolate-size: allow-keywords;
17 --doc-height: 100%;18 --doc-height: 100%;
18 --transparent: rgba(0, 0, 0, 0);19 --transparent: rgba(0, 0, 0, 0);
1920
@@ -122,7 +123,9 @@
122 --inline-avatar-small-factor: 0.6;123 --inline-avatar-small-factor: 0.6;
123124
124 --animation-duration: 125ms;125 --animation-duration: 125ms;
125 --animation-duration-slow: calc(var(--animation-duration) * 3);126 --animation-duration-2x: calc(var(--animation-duration) * 2);
127 --animation-duration-3x: calc(var(--animation-duration) * 3);
128 --animation-duration-slow: var(--animation-duration-3x);
126}129}
127130
128* {131* {
@@ -719,7 +722,7 @@ hr {
719 position: absolute;722 position: absolute;
720 width: 100%;723 width: 100%;
721 height: 100%;724 height: 100%;
722 transition: background-image 0.5s ease-in-out;725 transition: background-image var(--animation-duration-3x) ease-in-out;
723}726}
724727
725/* Background fitting options */728/* Background fitting options */
@@ -835,7 +838,7 @@ body.reduced-motion #bg_custom {
835 cursor: -webkit-grab;838 cursor: -webkit-grab;
836 display: none;839 display: none;
837 filter: drop-shadow(0px 0px 0px black);840 filter: drop-shadow(0px 0px 0px black);
838 transition: all 250ms;841 transition: all var(--animation-duration-2x);
839 font-size: calc(var(--mainFontSize)*1.3);842 font-size: calc(var(--mainFontSize)*1.3);
840}843}
841844
@@ -851,7 +854,7 @@ body .panelControlBar {
851 min-width: 55px;854 min-width: 55px;
852 justify-content: flex-end;855 justify-content: flex-end;
853 gap: 0px;856 gap: 0px;
854 transition: all 250ms;857 transition: all var(--animation-duration-2x);
855}858}
856859
857.zoomed_avatar .panelControlBar {860.zoomed_avatar .panelControlBar {
@@ -973,7 +976,7 @@ body .panelControlBar {
973 display: flex;976 display: flex;
974 align-items: center;977 align-items: center;
975 justify-content: center;978 justify-content: center;
976 transition: opacity 300ms;979 transition: opacity var(--animation-duration-2x);
977}980}
978981
979#rightSendForm>div:hover,982#rightSendForm>div:hover,
@@ -1000,7 +1003,7 @@ body .panelControlBar {
1000 padding-right: 2px;1003 padding-right: 2px;
1001 place-self: center;1004 place-self: center;
1002 cursor: pointer;1005 cursor: pointer;
1003 transition: 0.3s;1006 transition: var(--animation-duration-2x);
1004 opacity: 0.7;1007 opacity: 0.7;
1005}1008}
10061009
@@ -1047,7 +1050,7 @@ body .panelControlBar {
1047 padding-right: 2px;1050 padding-right: 2px;
1048 place-self: center;1051 place-self: center;
1049 cursor: pointer;1052 cursor: pointer;
1050 transition: 0.3s;1053 transition: var(--animation-duration-2x);
1051 opacity: 1;1054 opacity: 1;
1052 display: none;1055 display: none;
10531056
@@ -1094,7 +1097,7 @@ body .panelControlBar {
1094 z-index: 2001;1097 z-index: 2001;
1095 margin-left: 10px;1098 margin-left: 10px;
1096 padding: 0;1099 padding: 0;
1097 transition: 0.3s;1100 transition: var(--animation-duration-2x);
1098 display: flex;1101 display: flex;
1099 align-items: center;1102 align-items: center;
1100 order: 2;1103 order: 2;
@@ -1337,7 +1340,7 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
1337 border: var(--avatar-base-border-radius) solid var(--SmartThemeBodyColor);1340 border: var(--avatar-base-border-radius) solid var(--SmartThemeBodyColor);
1338 margin: var(--avatar-base-border-radius);1341 margin: var(--avatar-base-border-radius);
1339 cursor: pointer;1342 cursor: pointer;
1340 transition: filter 0.2s ease-in-out;1343 transition: filter var(--animation-duration) ease-in-out;
1341}1344}
13421345
1343.add_avatar:hover {1346.add_avatar:hover {
@@ -1350,7 +1353,7 @@ body.swipeAllMessages .mes:not(.last_mes) .swipes-counter {
1350 border-radius: var(--avatar-base-border-radius-round);1353 border-radius: var(--avatar-base-border-radius-round);
1351 border-style: none;1354 border-style: none;
1352 flex: 1;1355 flex: 1;
1353 transition: 250ms;1356 transition: var(--animation-duration-2x);
1354}1357}
13551358
1356.last_mes .mesAvatarWrapper {1359.last_mes .mesAvatarWrapper {
@@ -1542,7 +1545,7 @@ button {
1542 100% var(--progWidthClip),1545 100% var(--progWidthClip),
1543 100% 100%,1546 100% 100%,
1544 0% 100%);1547 0% 100%);
1545 transition: clip-path 200ms;1548 transition: clip-path var(--animation-duration-2x);
1546}1549}
15471550
1548#send_textarea:placeholder-shown {1551#send_textarea:placeholder-shown {
@@ -2493,8 +2496,8 @@ body[data-stscript-style] .hljs.language-stscript {
2493 margin: 3px;2496 margin: 3px;
2494 opacity: 0.75;2497 opacity: 0.75;
2495 filter: grayscale(1);2498 filter: grayscale(1);
2496 -webkit-transition: all 250ms ease-in-out;2499 -webkit-transition: all var(--animation-duration-2x) ease-in-out;
2497 transition: all 250ms ease-in-out;2500 transition: all var(--animation-duration-2x) ease-in-out;
2498}2501}
24992502
2500#character_popup .editor_maximize:hover {2503#character_popup .editor_maximize:hover {
@@ -2648,8 +2651,8 @@ input[type="file"] {
2648 margin-top: 0;2651 margin-top: 0;
2649 padding: 1px;2652 padding: 1px;
2650 filter: grayscale(1) brightness(75%);2653 filter: grayscale(1) brightness(75%);
2651 -webkit-transition: all 0.5s ease-in-out;2654 -webkit-transition: all var(--animation-duration-3x) ease-in-out;
2652 transition: all 0.5s ease-in-out;2655 transition: all var(--animation-duration-3x) ease-in-out;
2653}2656}
26542657
2655.right_menu_button:hover {2658.right_menu_button:hover {
@@ -2669,7 +2672,7 @@ input[type="file"] {
2669#lm_button_panel_pin_div,2672#lm_button_panel_pin_div,
2670#WI_button_panel_pin_div {2673#WI_button_panel_pin_div {
2671 opacity: 0.5;2674 opacity: 0.5;
2672 transition: 0.3s;2675 transition: var(--animation-duration-2x);
2673}2676}
26742677
2675#rm_button_panel_pin_div:hover,2678#rm_button_panel_pin_div:hover,
@@ -2805,7 +2808,6 @@ input[type="file"] {
2805}2808}
28062809
2807#rm_extensions_block {2810#rm_extensions_block {
2808 display: none;
2809 overflow-y: auto;2811 overflow-y: auto;
2810}2812}
28112813
@@ -2830,6 +2832,7 @@ input[type="file"] {
2830 margin: 0;2832 margin: 0;
2831 right: unset;2833 right: unset;
2832 width: calc(((100dvw - var(--sheldWidth)) / 2) - 1px);2834 width: calc(((100dvw - var(--sheldWidth)) / 2) - 1px);
2835 height: unset;
28332836
2834}2837}
28352838
@@ -2871,7 +2874,7 @@ input[type="file"] {
2871 aspect-ratio: 1 / 1;2874 aspect-ratio: 1 / 1;
2872 font-size: 20px;2875 font-size: 20px;
2873 opacity: 0.5;2876 opacity: 0.5;
2874 transition: all 250ms;2877 transition: all var(--animation-duration-2x);
2875}2878}
28762879
2877.floating_panel_maximize:hover,2880.floating_panel_maximize:hover,
@@ -2885,7 +2888,7 @@ input[type="file"] {
2885 aspect-ratio: 1 / 1;2888 aspect-ratio: 1 / 1;
2886 font-size: calc(var(--mainFontSize)*1.3);2889 font-size: calc(var(--mainFontSize)*1.3);
2887 opacity: 0.5;2890 opacity: 0.5;
2888 transition: all 250ms;2891 transition: all var(--animation-duration-2x);
2889 filter: drop-shadow(0px 0px 2px black);2892 filter: drop-shadow(0px 0px 2px black);
2890 text-shadow: none;2893 text-shadow: none;
2891}2894}
@@ -2934,7 +2937,6 @@ select option:not(:checked) {
2934/*#######################################################################*/2937/*#######################################################################*/
29352938
2936#rm_api_block {2939#rm_api_block {
2937 display: none;
2938 overflow-y: auto;2940 overflow-y: auto;
2939}2941}
29402942
@@ -3021,7 +3023,7 @@ select option:not(:checked) {
30213023
3022#rm_characters_block {3024#rm_characters_block {
3023 display: flex;3025 display: flex;
3024 overflow-y: auto;3026 overflow-y: hidden;
3025 flex-direction: column;3027 flex-direction: column;
3026 height: 100%;3028 height: 100%;
3027}3029}
@@ -3321,7 +3323,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
3321 text-shadow: none;3323 text-shadow: none;
3322 margin: 0;3324 margin: 0;
3323 filter: drop-shadow(0px 0px 3px white);3325 filter: drop-shadow(0px 0px 3px white);
3324 transition: opacity 0.2s ease-in-out;3326 transition: opacity var(--animation-duration-2x) ease-in-out;
3325 display: none;3327 display: none;
3326}3328}
33273329
@@ -3474,7 +3476,7 @@ input[type=search]:focus::-webkit-search-cancel-button {
3474 border: 1px solid var(--SmartThemeBorderColor);3476 border: 1px solid var(--SmartThemeBorderColor);
3475 border-radius: 10px;3477 border-radius: 10px;
3476 cursor: pointer;3478 cursor: pointer;
3477 transition: 0.2s;3479 transition: var(--animation-duration-2x);
3478}3480}
34793481
3480.avatar-container {3482.avatar-container {
@@ -3769,7 +3771,7 @@ grammarly-extension {
3769 width: min-content;3771 width: min-content;
3770 cursor: pointer;3772 cursor: pointer;
3771 margin: 5px 0;3773 margin: 5px 0;
3772 transition: 0.3s;3774 transition: var(--animation-duration-2x);
3773 display: flex;3775 display: flex;
3774 align-items: center;3776 align-items: center;
3775 justify-content: center;3777 justify-content: center;
@@ -3835,7 +3837,7 @@ grammarly-extension {
3835 background-color: var(--black30a);3837 background-color: var(--black30a);
3836 border: 1px solid var(--SmartThemeBorderColor);3838 border: 1px solid var(--SmartThemeBorderColor);
3837 cursor: grab;3839 cursor: grab;
3838 transition: background-color 200ms ease-in-out;3840 transition: background-color var(--animation-duration-2x) ease-in-out;
3839 position: relative;3841 position: relative;
3840 user-select: none;3842 user-select: none;
3841 display: flex;3843 display: flex;
@@ -4009,7 +4011,7 @@ input[type="checkbox"]::before {
4009 width: 0.65em;4011 width: 0.65em;
4010 height: 0.65em;4012 height: 0.65em;
4011 transform: scale(0);4013 transform: scale(0);
4012 transition: 120ms transform ease-in-out;4014 transition: var(--animation-duration) transform ease-in-out;
4013 box-shadow: inset 1em 1em var(--SmartThemeCheckboxTickColor);4015 box-shadow: inset 1em 1em var(--SmartThemeCheckboxTickColor);
4014 transform-origin: bottom left;4016 transform-origin: bottom left;
4015 clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);4017 clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
@@ -4181,6 +4183,13 @@ input[type='checkbox'].del_checkbox {
4181 background-repeat: no-repeat !important;4183 background-repeat: no-repeat !important;
4182 box-shadow: inset 0 0 2px var(--black50a) !important;4184 box-shadow: inset 0 0 2px var(--black50a) !important;
4183 cursor: ew-resize !important;4185 cursor: ew-resize !important;
4186}
4187
4188/*
4189 * Setting a z-index will allow scrollbar to display properly in Firefox,
4190 * while constraining to open drawers fixes a single frame linger in Chrome.
4191 */
4192.drawer-content.openDrawer .neo-range-slider {
4184 z-index: 1;4193 z-index: 1;
4185}4194}
41864195
@@ -4345,7 +4354,7 @@ input[type="range"]::-webkit-slider-thumb {
4345 border-radius: 100%;4354 border-radius: 100%;
4346 box-shadow: 0 0 3px black;4355 box-shadow: 0 0 3px black;
4347 */4356 */
4348 transition: all 250ms;4357 transition: all var(--animation-duration-2x);
4349}4358}
43504359
4351.topRightInset {4360.topRightInset {
@@ -4365,7 +4374,7 @@ input[type="range"]::-webkit-slider-thumb {
4365 font-weight: 400;4374 font-weight: 400;
4366 text-decoration: none;4375 text-decoration: none;
4367 border-bottom: 2px dashed red;4376 border-bottom: 2px dashed red;
4368 transition: 0.3s;4377 transition: var(--animation-duration-2x);
4369}4378}
43704379
4371.whitespacenowrap {4380.whitespacenowrap {
@@ -4398,7 +4407,7 @@ input[type="range"]::-webkit-slider-thumb {
4398 gap: 4px;4407 gap: 4px;
4399 flex-wrap: nowrap;4408 flex-wrap: nowrap;
4400 justify-content: flex-end;4409 justify-content: flex-end;
4401 transition: all 200ms;4410 transition: all var(--animation-duration-2x);
4402 overflow-x: hidden;4411 overflow-x: hidden;
4403 padding: 1px;4412 padding: 1px;
4404}4413}
@@ -4410,7 +4419,7 @@ input[type="range"]::-webkit-slider-thumb {
4410.mes_button,4419.mes_button,
4411.extraMesButtons>div {4420.extraMesButtons>div {
4412 cursor: pointer;4421 cursor: pointer;
4413 transition: opacity 0.2s ease-in-out;4422 transition: opacity var(--animation-duration-2x) ease-in-out;
4414 filter: drop-shadow(0px 0px 2px black);4423 filter: drop-shadow(0px 0px 2px black);
4415 opacity: 0.3;4424 opacity: 0.3;
4416 padding: 1px 3px;4425 padding: 1px 3px;
@@ -4441,7 +4450,7 @@ input[type="range"]::-webkit-slider-thumb {
4441 cursor: pointer;4450 cursor: pointer;
4442 padding-bottom: 5px;4451 padding-bottom: 5px;
4443 filter: drop-shadow(0px 0px 2px black);4452 filter: drop-shadow(0px 0px 2px black);
4444 transition: 0.3s ease-in-out;4453 transition: var(--animation-duration-2x) ease-in-out;
4445}4454}
44464455
4447.mes_reasoning_actions {4456.mes_reasoning_actions {
@@ -4776,7 +4785,7 @@ body .ui-slider-handle {
4776 text-align: center;4785 text-align: center;
4777 margin-left: 0;4786 margin-left: 0;
4778 opacity: 1 !important;4787 opacity: 1 !important;
4779 transition: filter 200ms;4788 transition: filter var(--animation-duration-2x);
4780 filter: brightness(1.2);4789 filter: brightness(1.2);
4781}4790}
47824791
@@ -4834,7 +4843,7 @@ body .ui-widget-content li {
4834 align-items: center;4843 align-items: center;
4835 cursor: pointer;4844 cursor: pointer;
4836 opacity: 0.5;4845 opacity: 0.5;
4837 transition: opacity 200ms;4846 transition: opacity var(--animation-duration-2x);
4838}4847}
48394848
4840body .ui-widget-content li:hover {4849body .ui-widget-content li:hover {
@@ -4996,34 +5005,6 @@ a:hover {
4996/* Right nav panel and nav-toggle */5005/* Right nav panel and nav-toggle */
4997/* ############################################################# */5006/* ############################################################# */
49985007
4999#right-nav-panel {
5000 width: calc((100vw - var(--sheldWidth) - 2px) /2);
5001 width: calc((100dvw - var(--sheldWidth) - 2px) /2);
5002 max-height: calc(100vh - var(--topBarBlockSize));
5003 max-height: calc(100dvh - var(--topBarBlockSize));
5004 height: calc(100vh - var(--topBarBlockSize));
5005 height: calc(100dvh - var(--topBarBlockSize));
5006 position: fixed;
5007 top: 0;
5008 margin: 0;
5009 right: 0;
5010 left: calc(100% - var(--sheldWidth) /2 + var(--sheldWidth) +1px);
5011 padding: 5px;
5012 margin-bottom: 5px;
5013 backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
5014 background-color: var(--SmartThemeBlurTintColor);
5015 -webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
5016 z-index: 3000;
5017
5018 border: 1px solid var(--SmartThemeBorderColor);
5019
5020 box-shadow: none;
5021 border-radius: 10px;
5022 overflow: hidden;
5023 flex-flow: column;
5024 min-width: 100px;
5025}
5026
5027#nav-toggle {5008#nav-toggle {
5028 position: fixed;5009 position: fixed;
5029 right: 13px;5010 right: 13px;
@@ -5047,7 +5028,7 @@ a:hover {
5047 display: none;5028 display: none;
5048 position: relative;5029 position: relative;
5049 width: fit-content;5030 width: fit-content;
5050 transition: all 0.1s;5031 transition: all var(--animation-duration);
5051 padding: 0.5rem;5032 padding: 0.5rem;
5052}5033}
50535034
@@ -5239,7 +5220,7 @@ body:not(.sd) .mes_img_swipes {
5239 border-radius: 10px;5220 border-radius: 10px;
5240 padding: 2px 5px;5221 padding: 2px 5px;
5241 border: 1px solid var(--SmartThemeBorderColor);5222 border: 1px solid var(--SmartThemeBorderColor);
5242 transition: all 250ms;5223 transition: all var(--animation-duration-2x);
5243}5224}
52445225
5245.standoutHeader.inline-drawer-header {5226.standoutHeader.inline-drawer-header {
@@ -5322,12 +5303,12 @@ body:has(#character_popup.open) #top-settings-holder:has(.drawer-content.openDra
5322}5303}
53235304
5324.drawer-icon.openIcon {5305.drawer-icon.openIcon {
5325 transition: all 0.275s;5306 transition: all var(--animation-duration-2x);
5326}5307}
53275308
5328.drawer-icon.closedIcon {5309.drawer-icon.closedIcon {
5329 opacity: 0.3;5310 opacity: 0.3;
5330 transition: all 0.275s;5311 transition: all var(--animation-duration-2x);
5331}5312}
53325313
5333.drawer-icon.closedIcon:hover {5314.drawer-icon.closedIcon:hover {
@@ -5390,7 +5371,7 @@ body:has(#character_popup.open) #top-settings-holder:has(.drawer-content.openDra
5390 border: 1px solid var(--SmartThemeBorderColor);5371 border: 1px solid var(--SmartThemeBorderColor);
5391 min-width: 450px;5372 min-width: 450px;
5392 width: var(--sheldWidth);5373 width: var(--sheldWidth);
5393 overflow-y: auto;5374 overflow-y: hidden;
5394 max-height: calc(100vh - calc(var(--topBarBlockSize) + var(--bottomFormBlockSize)));5375 max-height: calc(100vh - calc(var(--topBarBlockSize) + var(--bottomFormBlockSize)));
5395 max-height: calc(100dvh - calc(var(--topBarBlockSize) + var(--bottomFormBlockSize)));5376 max-height: calc(100dvh - calc(var(--topBarBlockSize) + var(--bottomFormBlockSize)));
5396 display: none;5377 display: none;
@@ -5399,9 +5380,30 @@ body:has(#character_popup.open) #top-settings-holder:has(.drawer-content.openDra
5399 left: 0;5380 left: 0;
5400 right: 0;5381 right: 0;
5401 margin: 0 auto;5382 margin: 0 auto;
5383 height: 0;
5402 backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));5384 backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
5403 -webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));5385 -webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
5404 /* z-index: 1000 !important; */5386 /* z-index: 1000 !important; */
5387 transition-property: height, display;
5388 transition-duration: var(--animation-duration-2x);
5389 transition-timing-function: ease-in-out;
5390 transition-behavior: allow-discrete;
5391}
5392
5393#movingDivs>.drawer-content {
5394 height: unset;
5395}
5396
5397.drawer-content.openDrawer {
5398 display: block;
5399 height: auto;
5400 height: calc-size(auto, size);
5401 overflow-y: auto;
5402 animation: hide-scroll var(--animation-duration-2x) backwards;
5403
5404 @starting-style {
5405 height: 0;
5406 }
5405}5407}
54065408
5407body.movingUI .inline-drawer-maximize {5409body.movingUI .inline-drawer-maximize {
@@ -5428,8 +5430,7 @@ body:not(.movingUI) .drawer-content.maximized {
5428.fillLeft {5430.fillLeft {
5429 width: calc((100vw - var(--sheldWidth) - 2px) /2);5431 width: calc((100vw - var(--sheldWidth) - 2px) /2);
5430 width: calc((100dvw - var(--sheldWidth) - 2px) /2);5432 width: calc((100dvw - var(--sheldWidth) - 2px) /2);
5431 height: calc(100vh - var(--topBarBlockSize));5433 height: 0;
5432 height: calc(100dvh - var(--topBarBlockSize));
5433 max-height: calc(100vh - var(--topBarBlockSize));5434 max-height: calc(100vh - var(--topBarBlockSize));
5434 max-height: calc(100dvh - var(--topBarBlockSize));5435 max-height: calc(100dvh - var(--topBarBlockSize));
5435 position: fixed;5436 position: fixed;
@@ -5443,6 +5444,66 @@ body:not(.movingUI) .drawer-content.maximized {
5443 border: 1px solid var(--SmartThemeBorderColor);5444 border: 1px solid var(--SmartThemeBorderColor);
5444}5445}
54455446
5447.fillLeft.openDrawer {
5448 overflow-y: hidden;
5449 height: calc(100vh - var(--topBarBlockSize));
5450 height: calc(100dvh - var(--topBarBlockSize));
5451 height: calc-size((100dvh - var(--topBarBlockSize)), size);
5452
5453 @starting-style {
5454 height: 0;
5455 }
5456}
5457
5458.fillRight {
5459 width: calc((100vw - var(--sheldWidth) - 2px) /2);
5460 width: calc((100dvw - var(--sheldWidth) - 2px) /2);
5461 max-height: calc(100vh - var(--topBarBlockSize));
5462 max-height: calc(100dvh - var(--topBarBlockSize));
5463 height: 0;
5464 position: fixed;
5465 top: 0;
5466 margin: 0;
5467 right: 0;
5468 left: calc(100% - var(--sheldWidth) /2 + var(--sheldWidth) +1px);
5469 padding: 5px;
5470 margin-bottom: 5px;
5471 backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
5472 background-color: var(--SmartThemeBlurTintColor);
5473 -webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)));
5474 z-index: 3000;
5475
5476 border: 1px solid var(--SmartThemeBorderColor);
5477
5478 box-shadow: none;
5479 border-radius: 10px;
5480 overflow: hidden;
5481 flex-flow: column;
5482 min-width: 100px;
5483}
5484
5485.fillRight.openDrawer {
5486 display: flex;
5487 height: calc(100vh - var(--topBarBlockSize));
5488 height: calc(100dvh - var(--topBarBlockSize));
5489 height: calc-size((100dvh - var(--topBarBlockSize)), size);
5490
5491 @starting-style {
5492 height: 0;
5493 }
5494}
5495
5496/*
5497.fillRight #rm_print_characters_block {
5498 overflow-y: hidden;
5499}
5500
5501.fillRight.openDrawer #rm_print_characters_block {
5502 overflow-y: auto;
5503 animation: hide-scroll var(--animation-duration-2x) backwards;
5504}
5505*/
5506
5446.scrollableInner {5507.scrollableInner {
5447 overflow-x: hidden;5508 overflow-x: hidden;
5448 height: calc(100% - 30px);5509 height: calc(100% - 30px);
@@ -5709,7 +5770,7 @@ body:not(.movingUI) .drawer-content.maximized {
5709 aspect-ratio: 1 / 1;5770 aspect-ratio: 1 / 1;
5710 font-size: 20px;5771 font-size: 20px;
5711 opacity: 0.5;5772 opacity: 0.5;
5712 transition: all 250ms;5773 transition: all var(--animation-duration-2x);
5713}5774}
57145775
5715/* #groupMemberListPopout,5776/* #groupMemberListPopout,
@@ -5796,7 +5857,7 @@ body:not(.movingUI) .drawer-content.maximized {
5796 color: var(--SmartThemeBodyColor);5857 color: var(--SmartThemeBodyColor);
5797 border: 1px solid var(--SmartThemeBorderColor);5858 border: 1px solid var(--SmartThemeBorderColor);
5798 border-radius: 5px;5859 border-radius: 5px;
5799 transition: opacity 0.2s;5860 transition: opacity var(--animation-duration-2x);
5800 opacity: 0.8;5861 opacity: 0.8;
5801 cursor: pointer;5862 cursor: pointer;
5802}5863}
@@ -5988,7 +6049,7 @@ body:not(.movingUI) .drawer-content.maximized {
5988 margin: 7px;6049 margin: 7px;
5989 width: calc(100% - 7px);6050 width: calc(100% - 7px);
5990 box-sizing: border-box;6051 box-sizing: border-box;
5991 transition: transform 0.2s ease-in-out, background-color 0.2s ease-in-out, border 0.2s ease-in-out;6052 transition: transform var(--animation-duration-2x) ease-in-out, background-color var(--animation-duration-2x) ease-in-out, border var(--animation-duration-2x) ease-in-out;
5992}6053}
59936054
5994.model-card .details-container {6055.model-card .details-container {
@@ -6000,7 +6061,7 @@ body:not(.movingUI) .drawer-content.maximized {
6000 transform: scale(1.01);6061 transform: scale(1.01);
6001 background-color: #444;6062 background-color: #444;
6002 /* Smooth transition */6063 /* Smooth transition */
6003 transition: transform 0.2s ease-in-out, background-color 0.2s ease-in-out;6064 transition: transform var(--animation-duration-2x) ease-in-out, background-color var(--animation-duration-2x) ease-in-out;
6004}6065}
60056066
6006.model-card.selected {6067.model-card.selected {