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

f33db29e76f474e4f938720d2ca06ba556af1d12

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

Signed
27 files changed, +1270 -1151Showing whitespace changes
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+85 -77
@@ -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>5461 </div>
5462 <hr class="wide100p">
5462 </div>5463 </div>
5463 <hr>5464 </div>
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,6 +5487,8 @@
5485 </div>5487 </div>
5486 </div>5488 </div>
5487 <!-- end group peeking cope structure-->5489 <!-- end group peeking cope structure-->
5490
5491 <div class="scrollableInner">
5488 <div name="Solo Char Create/Edit Panel" id="rm_ch_create_block" class="right_menu flex-container flexFlowColumn" style="display: none;">5492 <div name="Solo Char Create/Edit Panel" id="rm_ch_create_block" class="right_menu flex-container flexFlowColumn" style="display: none;">
5489 <form id="form_create" action="javascript:void(null);" method="post" enctype="multipart/form-data">5493 <form id="form_create" action="javascript:void(null);" method="post" enctype="multipart/form-data">
5490 <div id="avatar-and-name-block">5494 <div id="avatar-and-name-block">
@@ -5820,6 +5824,7 @@
5820 </div>5824 </div>
5821 <div id="rm_print_characters_block" class="flexFlowColumn"></div>5825 <div id="rm_print_characters_block" class="flexFlowColumn"></div>
5822 </div>5826 </div>
5827 </div>
5823 </nav>5828 </nav>
5824 </div>5829 </div>
5825 </div>5830 </div>
@@ -6195,79 +6200,7 @@
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">
6198 <div class="world_entry">6203 <div class="world_entry_edit">
6199 <form class="world_entry_form wi-card-entry">
6200 <div class="inline-drawer wide100p">
6201 <div class="inline-drawer-header gap5px padding0">
6202 <span class="drag-handle">&#9776;</span>
6203 <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">
6204 <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
6205 <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" data-i18n="[title]Toggle entry's active state." title="Toggle entry's active state."></div>
6206 <div class="flex-container alignitemscenter wide100p">
6207 <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">
6208 <div class="flex-container flex1">
6209 <textarea class="text_pole" rows="1" name="comment" data-i18n="[placeholder]Entry Title/Memo" placeholder="Entry Title/Memo"></textarea>
6210 </div>
6211 <!-- <span class="world_entry_form_position_value"></span> -->
6212 <select data-i18n="[title]WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized" title="WI Entry Status:&#13;🔵 Constant&#13;🟢 Normal&#13;🔗 Vectorized" name="entryStateSelector" class="text_pole widthNatural margin0">
6213 <option value="constant" title="Constant" data-i18n="[title]WI_Entry_Status_Constant">🔵</option>
6214 <option value="normal" title="Normal" data-i18n="[title]WI_Entry_Status_Normal">🟢</option>
6215 <option value="vectorized" title="Vectorized" data-i18n="[title]WI_Entry_Status_Vectorized">🔗</option>
6216 </select>
6217 </div>
6218 <div class="WIEnteryHeaderControls flex-container">
6219 <div name="PositionBlock" class="world_entry_form_control world_entry_form_radios wi-enter-footer-text">
6220 <label for="position" class="WIEntryHeaderTitleMobile" data-i18n="Position:">Position:</label>
6221 <select name="position" class="text_pole widthNatural margin0" data-i18n="[title]T_Position" title="↑Char: Before Character Definitions&#13;↓Char: After Character Definitions&#13;↑EM: Before Example Messages&#13;↓EM: After Example Messages&#13;↑AN: Before Author's Note&#13;↓AN: After Author's Note&#13;@D ⚙️: at Depth (System)&#13;@D 👤: at Depth (User)&#13;@D 🤖: at Depth (Assistant)">
6222 <option value="0" data-role="" data-i18n="Before Char Defs">
6223 ↑Char
6224 </option>
6225 <option value="1" data-role="" data-i18n="After Char Defs">
6226 ↓Char
6227 </option>
6228 <option value="5" data-role="" data-i18n="Before EM">
6229 ↑EM
6230 </option>
6231 <option value="6" data-role="" data-i18n="After EM">
6232 ↓EM
6233 </option>
6234 <option value="2" data-role="" data-i18n="Before AN">
6235 ↑AN
6236 </option>
6237 <option value="3" data-role="" data-i18n="After AN">
6238 ↓AN
6239 </option>
6240 <option value="4" data-role="0" data-i18n="at Depth System">
6241 @D ⚙️
6242 </option>
6243 <option value="4" data-role="1" data-i18n="at Depth User">
6244 @D 👤
6245 </option>
6246 <option value="4" data-role="2" data-i18n="at Depth AI">
6247 @D 🤖
6248 </option>
6249 </select>
6250 </div>
6251 <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6252 <label for="depth" class="WIEntryHeaderTitleMobile" data-i18n="Depth:">Depth:</label>
6253 <input title="Depth" class="text_pole wideMax100px margin0" type="number" name="depth" data-i18n="[title]Depth" placeholder="" min="0" max="9999" />
6254 </div>
6255 <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6256 <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Order:">Order:</label>
6257 <input title="Order" data-i18n="[title]Order" class="text_pole wideMax100px margin0" type="number" name="order" placeholder="" min="0" max="9999" />
6258 </div>
6259 <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap probabilityContainer">
6260 <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Trigger %:">Trigger %:</label>
6261 <input title="Probability" data-i18n="[title]Probability" class="text_pole wideMax100px margin0" type="number" name="probability" placeholder="" min="0" max="100" />
6262 </div>
6263 </div>
6264 </div>
6265 </div>
6266 <i class="menu_button move_entry_button fa-solid fa-right-left" title="Move Entry to Another Lorebook" data-i18n="[title]Move Entry to Another Lorebook"></i>
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>
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>
6269 </div>
6270 <div class="inline-drawer-content flex-container paddingBottom5px wide100p">
6271 <div class="flex-container wide100p alignitemscenter">6204 <div class="flex-container wide100p alignitemscenter">
6272 <div name="keywordsAndLogicBlock" class="flex-container wide100p alignitemscenter">6205 <div name="keywordsAndLogicBlock" class="flex-container wide100p alignitemscenter">
6273 <div class="world_entry_form_control keyprimary flex1">6206 <div class="world_entry_form_control keyprimary flex1">
@@ -6364,13 +6297,13 @@
6364 </span>6297 </span>
6365 <div>6298 <div>
6366 <label class="checkbox flex-container alignitemscenter flexNoGap">6299 <label class="checkbox flex-container alignitemscenter flexNoGap">
6367 <input type="checkbox" name="exclude_recursion" />6300 <input type="checkbox" name="excludeRecursion" />
6368 <span data-i18n="Non-recursable (will not be activated by another)">6301 <span data-i18n="Non-recursable (will not be activated by another)">
6369 Non-recursable (will not be activated by another)6302 Non-recursable (will not be activated by another)
6370 </span>6303 </span>
6371 </label>6304 </label>
6372 <label class="checkbox flex-container alignitemscenter flexNoGap">6305 <label class="checkbox flex-container alignitemscenter flexNoGap">
6373 <input type="checkbox" name="prevent_recursion" />6306 <input type="checkbox" name="preventRecursion" />
6374 <span data-i18n="Prevent further recursion (this entry will not activate others)">6307 <span data-i18n="Prevent further recursion (this entry will not activate others)">
6375 Prevent further recursion (will not activate others)6308 Prevent further recursion (will not activate others)
6376 </span>6309 </span>
@@ -6559,6 +6492,81 @@
6559 </div>6492 </div>
6560 </div>6493 </div>
6561 </div>6494 </div>
6495 <div class="world_entry">
6496 <form class="world_entry_form wi-card-entry">
6497 <div class="inline-drawer wide100p">
6498 <div class="inline-drawer-header gap5px padding0">
6499 <span class="drag-handle">&#9776;</span>
6500 <div class="gap5px world_entry_thin_controls wide100p alignitemscenter">
6501 <div class="inline-drawer-toggle fa-fw fa-solid fa-circle-chevron-down inline-drawer-icon down"></div>
6502 <div class="fa-solid fa-toggle-on killSwitch" name="entryKillSwitch" data-i18n="[title]Toggle entry's active state." title="Toggle entry's active state."></div>
6503 <div class="flex-container alignitemscenter wide100p">
6504 <div class="WIEntryTitleAndStatus flex-container flex1 alignitemscenter">
6505 <div class="flex-container flex1">
6506 <textarea class="text_pole" rows="1" name="comment" data-i18n="[placeholder]Entry Title/Memo" placeholder="Entry Title/Memo"></textarea>
6507 </div>
6508 <!-- <span class="world_entry_form_position_value"></span> -->
6509 <select data-i18n="[title]WI Entry Status:🔵 Constant🟢 Normal🔗 Vectorized" title="WI Entry Status:&#13;🔵 Constant&#13;🟢 Normal&#13;🔗 Vectorized" name="entryStateSelector" class="text_pole widthNatural margin0">
6510 <option value="constant" title="Constant" data-i18n="[title]WI_Entry_Status_Constant">🔵</option>
6511 <option value="normal" title="Normal" data-i18n="[title]WI_Entry_Status_Normal">🟢</option>
6512 <option value="vectorized" title="Vectorized" data-i18n="[title]WI_Entry_Status_Vectorized">🔗</option>
6513 </select>
6514 </div>
6515 <div class="WIEnteryHeaderControls flex-container">
6516 <div name="PositionBlock" class="world_entry_form_control world_entry_form_radios wi-enter-footer-text">
6517 <label for="position" class="WIEntryHeaderTitleMobile" data-i18n="Position:">Position:</label>
6518 <select name="position" class="text_pole widthNatural margin0" data-i18n="[title]T_Position" title="↑Char: Before Character Definitions&#13;↓Char: After Character Definitions&#13;↑EM: Before Example Messages&#13;↓EM: After Example Messages&#13;↑AN: Before Author's Note&#13;↓AN: After Author's Note&#13;@D ⚙️: at Depth (System)&#13;@D 👤: at Depth (User)&#13;@D 🤖: at Depth (Assistant)">
6519 <option value="0" data-role="" data-i18n="Before Char Defs">
6520 ↑Char
6521 </option>
6522 <option value="1" data-role="" data-i18n="After Char Defs">
6523 ↓Char
6524 </option>
6525 <option value="5" data-role="" data-i18n="Before EM">
6526 ↑EM
6527 </option>
6528 <option value="6" data-role="" data-i18n="After EM">
6529 ↓EM
6530 </option>
6531 <option value="2" data-role="" data-i18n="Before AN">
6532 ↑AN
6533 </option>
6534 <option value="3" data-role="" data-i18n="After AN">
6535 ↓AN
6536 </option>
6537 <option value="4" data-role="0" data-i18n="at Depth System">
6538 @D ⚙️
6539 </option>
6540 <option value="4" data-role="1" data-i18n="at Depth User">
6541 @D 👤
6542 </option>
6543 <option value="4" data-role="2" data-i18n="at Depth AI">
6544 @D 🤖
6545 </option>
6546 </select>
6547 </div>
6548 <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6549 <label for="depth" class="WIEntryHeaderTitleMobile" data-i18n="Depth:">Depth:</label>
6550 <input title="Depth" class="text_pole wideMax100px margin0" type="number" name="depth" data-i18n="[title]Depth" placeholder="" min="0" max="9999" />
6551 </div>
6552 <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap">
6553 <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Order:">Order:</label>
6554 <input title="Order" data-i18n="[title]Order" class="text_pole wideMax100px margin0" type="number" name="order" placeholder="" min="0" max="9999" />
6555 </div>
6556 <div class="world_entry_form_control wi-enter-footer-text flex-container flexNoGap probabilityContainer">
6557 <label for="order" class="WIEntryHeaderTitleMobile" data-i18n="Trigger %:">Trigger %:</label>
6558 <input title="Probability" data-i18n="[title]Probability" class="text_pole wideMax100px margin0" type="number" name="probability" placeholder="" min="0" max="100" />
6559 </div>
6560 </div>
6561 </div>
6562 </div>
6563 <i class="menu_button move_entry_button fa-solid fa-right-left" title="Move Entry to Another Lorebook" data-i18n="[title]Move Entry to Another Lorebook"></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>
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>
6566 </div>
6567 <div class="inline-drawer-content inline-drawer-outlet flex-container paddingBottom5px wide100p ">
6568 <!-- Outlet for world_entry_edit -->
6569 </div>
6562 </div>6570 </div>
6563 </form>6571 </form>
6564 </div>6572 </div>
public/script.js+32 -82
@@ -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) => {
10350 slideToggle(el, {
10351 ...getSlideToggleOptions(),
10352 elementDisplayStyle: 'flex',
10353 onAnimationEnd: function (el) {
10354 el.closest('.drawer-content').classList.remove('resizing');
10355 favsToHotswap();10346 favsToHotswap();
10356 $('#rm_print_characters_block').trigger('scroll');10347 $('#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 });
10377 }10355 }
10378
10379 } else if (drawerWasOpenAlready) { //to close manually
10380 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 }10356 }
10392 else {10357 } else if (drawerWasOpenAlready) {
10393 $('.openDrawer').not('.pinnedOpen').addClass('resizing').each((_, el) => {10358 icon.toggleClass('closedIcon openIcon');
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, {
12066 ...getSlideToggleOptions(),
12067 onAnimationEnd: (el) => {
12068 el.closest('.drawer-content').classList.remove('resizing');
12069 },
12070 });
12071 });
12072 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');12022 $('.openIcon').not('.drawerPinnedOpen').toggleClass('closedIcon openIcon');
12073 $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer');12023 $openDrawers.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 }
555
556 // Prevent resizing offscreen
557 if (top + elmnt.height() >= winHeight) {
558 elmnt.css('height', winHeight - top - 1 + 'px');
559 elmnt.css('width', (winHeight - top - 1) / imageAspectRatio + 'px');
560 }563 }
561564 if (top + $elmnt.height() >= winHeight) {
562 if (left + elmnt.width() >= winWidth) {565 $elmnt.css('height', winHeight - top - 1 + 'px');
563 elmnt.css('width', winWidth - left - 1 + 'px');566 $elmnt.css('width', (winHeight - top - 1) / imageAspectRatio + 'px');
564 elmnt.css('height', (winWidth - left - 1) * imageAspectRatio + 'px');
565 }567 }
566 } else { //prevent divs that are not zoomedAvatars from resizing offscreen568 if (left + $elmnt.width() >= winWidth) {
567569 $elmnt.css('width', winWidth - left - 1 + 'px');
568 if (top + elmnt.height() >= winHeight) {570 $elmnt.css('height', (winWidth - left - 1) * imageAspectRatio + 'px');
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 }
581
582 //set css to prevent weird resize behavior (does not save)
583 elmnt.css('left', left);
584 elmnt.css('top', top);
585
586 //set a listener for mouseup to save new width/height
587 $(window).off('mouseup').on('mouseup', () => {
588 console.log(`Saving ${elmntName} Height/Width`);
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 }578 }
594579 $elmnt.css({ left, top });
595 power_user.movingUIState[elmntName].width = width;580 $elmnt.off('mouseup').on('mouseup', () => {
596 power_user.movingUIState[elmntName].height = height;581 if (
597 eventSource.emit('resizeUI', elmntName);582 power_user.movingUIState[elmntName].width === $elmnt.width() &&
598 saveSettingsDebounced();583 power_user.movingUIState[elmntName].height === $elmnt.height()
599 imgHeight = null;584 ) return;
600 imgWidth = null;585 savePositionAndSize();
601 height = null;586 observer.disconnect();
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();
701 top = null;
702 left = null;
703 right = null;
704 bottom = null;
705 maxX = null;
706 maxY = null;
707 }632 }
633
634 // Setup event listeners
635 if ($elmntHeader.length) {
636 $elmntHeader.off('mousedown').on('mousedown', (e) => {
637 if ($(e.target).hasClass('drag-grabber')) {
638 actionType = 'drag';
639 isMouseDown = true;
640 observer.observe($elmnt[0], { attributes: true, attributeFilter: ['style'] });
641 dragMouseDown(e);
642 }
643 });
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+11 -14
@@ -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;
@@ -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+201 -37
@@ -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,6 +416,7 @@
384 align-items: baseline;416 align-items: baseline;
385 }417 }
386 }418 }
419
387 >#qr--autoExec {420 >#qr--autoExec {
388 .checkbox_label {421 .checkbox_label {
389 text-wrap: nowrap;422 text-wrap: nowrap;
@@ -433,44 +466,52 @@
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;
487
451 >#qr--main,488 >#qr--main,
452 >#qr--resizeHandle,489 >#qr--resizeHandle,
453 >#qr--qrOptions>h3,490 >#qr--qrOptions>h3,
454 >#qr--qrOptions>#qr--modal-executeButtons,491 >#qr--qrOptions>#qr--modal-executeButtons,
455 > #qr--qrOptions > #qr--modal-executeProgress492 >#qr--qrOptions>#qr--modal-executeProgress {
456 {
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 }
514
474 &:has(.qr--isExecuting) {515 &:has(.qr--isExecuting) {
475 .popup-controls {516 .popup-controls {
476 display: none;517 display: none;
@@ -481,10 +522,12 @@
481 z-index: 50000;522 z-index: 50000;
482 pointer-events: none;523 pointer-events: none;
483 background-color: rgb(47 150 180 / 0.5);524 background-color: rgb(47 150 180 / 0.5);
525
484 &.qr--unresolved {526 &.qr--unresolved {
485 background-color: rgb(255 255 0 / 0.5);527 background-color: rgb(255 255 0 / 0.5);
486 }528 }
487 }529 }
530
488 .qr--highlight-secondary {531 .qr--highlight-secondary {
489 position: absolute;532 position: absolute;
490 z-index: 50000;533 z-index: 50000;
@@ -505,6 +548,7 @@
505 overflow: hidden;548 overflow: hidden;
506549
507 &.qr--isExecuting {550 &.qr--isExecuting {
551
508 #qr--main>h3:first-child,552 #qr--main>h3:first-child,
509 #qr--main>.qr--labels,553 #qr--main>.qr--labels,
510 #qr--main>.qr--modal-messageContainer>.qr--modal-editorSettings,554 #qr--main>.qr--modal-messageContainer>.qr--modal-editorSettings,
@@ -512,28 +556,32 @@
512 #qr--qrOptions>#qr--ctxEditor,556 #qr--qrOptions>#qr--ctxEditor,
513 #qr--qrOptions>.qr--ctxEditorActions,557 #qr--qrOptions>.qr--ctxEditorActions,
514 #qr--qrOptions>.qr--ctxEditorActions+h3,558 #qr--qrOptions>.qr--ctxEditorActions+h3,
515 #qr--qrOptions > .qr--ctxEditorActions + h3 + div559 #qr--qrOptions>.qr--ctxEditorActions+h3+div {
516 {
517 display: none;560 display: none;
518 }561 }
562
519 #qr--main>.qr--modal-messageContainer>#qr--modal-messageHolder>#qr--modal-message {563 #qr--main>.qr--modal-messageContainer>#qr--modal-messageHolder>#qr--modal-message {
520 visibility: hidden;564 visibility: hidden;
521 }565 }
566
522 #qr--modal-debugButtons {567 #qr--modal-debugButtons {
523 display: flex;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,31 +589,38 @@
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 }
610
558 #qr--resizeHandle {611 #qr--resizeHandle {
559 width: 6px;612 width: 6px;
560 background-color: var(--SmartThemeBorderColor);613 background-color: var(--SmartThemeBorderColor);
561 border: 2px solid var(--SmartThemeBlurTintColor);614 border: 2px solid var(--SmartThemeBlurTintColor);
562 transition: border-color 200ms, background-color 200ms;615 transition: border-color var(--animation-duration-2x), background-color var(--animation-duration-2x);
563 cursor: w-resize;616 cursor: w-resize;
617
564 &:hover {618 &:hover {
565 background-color: var(--SmartThemeQuoteColor);619 background-color: var(--SmartThemeQuoteColor);
566 border-color: var(--SmartThemeQuoteColor);620 border-color: var(--SmartThemeQuoteColor);
567 }621 }
568 }622 }
623
569 #qr--qrOptions {624 #qr--qrOptions {
570 width: var(--width, auto);625 width: var(--width, auto);
571 }626 }
@@ -576,38 +631,48 @@
576 display: flex;631 display: flex;
577 flex-direction: column;632 flex-direction: column;
578 overflow: hidden;633 overflow: hidden;
634
579 >.qr--labels {635 >.qr--labels {
580 flex: 0 0 auto;636 flex: 0 0 auto;
581 display: flex;637 display: flex;
582 flex-direction: row;638 flex-direction: row;
583 gap: 0.5em;639 gap: 0.5em;
584 padding: 1px;640 padding: 1px;
585 > label, > .label {641
642 >label,
643 >.label {
586 flex: 1 1 1px;644 flex: 1 1 1px;
587 display: flex;645 display: flex;
588 flex-direction: column;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 }
663
602 .qr--labelText {664 .qr--labelText {
603 flex: 1 1 auto;665 flex: 1 1 auto;
604 }666 }
667
605 .qr--labelHint {668 .qr--labelHint {
606 flex: 1 1 auto;669 flex: 1 1 auto;
607 }670 }
671
608 input {672 input {
609 flex: 0 0 auto;673 flex: 0 0 auto;
610 }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: "[";
658 }732 }
733
734 &:after {
735 content: "]";
736 }
737 }
738
659 .qr--message {739 .qr--message {
660 height: 1lh;740 height: 1lh;
661 overflow: hidden;741 overflow: hidden;
@@ -666,11 +746,13 @@
666 }746 }
667 }747 }
668 }748 }
749
669 >.qr--modal-messageContainer {750 >.qr--modal-messageContainer {
670 flex: 1 1 auto;751 flex: 1 1 auto;
671 display: flex;752 display: flex;
672 flex-direction: column;753 flex-direction: column;
673 overflow: hidden;754 overflow: hidden;
755
674 >.qr--modal-editorSettings {756 >.qr--modal-editorSettings {
675 display: flex;757 display: flex;
676 flex-wrap: wrap;758 flex-wrap: wrap;
@@ -679,38 +761,48 @@
679 color: var(--grey70);761 color: var(--grey70);
680 font-size: smaller;762 font-size: smaller;
681 align-items: baseline;763 align-items: baseline;
764
682 >.checkbox_label {765 >.checkbox_label {
683 white-space: nowrap;766 white-space: nowrap;
767
684 >input {768 >input {
685 font-size: inherit;769 font-size: inherit;
686 }770 }
687 }771 }
688 }772 }
773
689 >#qr--modal-messageHolder {774 >#qr--modal-messageHolder {
690 flex: 1 1 auto;775 flex: 1 1 auto;
691 display: grid;776 display: grid;
692 text-align: left;777 text-align: left;
693 overflow: hidden;778 overflow: hidden;
779
694 &.qr--noSyntax {780 &.qr--noSyntax {
695 >#qr--modal-messageSyntax {781 >#qr--modal-messageSyntax {
696 display: none;782 display: none;
697 }783 }
784
698 >#qr--modal-message {785 >#qr--modal-message {
699 background-color: var(--ac-style-color-background);786 background-color: var(--ac-style-color-background);
700 color: var(--ac-style-color-text);787 color: var(--ac-style-color-text);
701 &::-webkit-scrollbar, &::-webkit-scrollbar-thumb {788
789 &::-webkit-scrollbar,
790 &::-webkit-scrollbar-thumb {
702 visibility: visible;791 visibility: visible;
703 cursor: unset;792 cursor: unset;
704 }793 }
794
705 &::selection {795 &::selection {
706 color: unset;796 color: unset;
707 background-color: rgba(108 171 251 / 0.25);797 background-color: rgba(108 171 251 / 0.25);
798
708 @supports (color: rgb(from white r g b / 0.25)) {799 @supports (color: rgb(from white r g b / 0.25)) {
709 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);800 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);
710 }801 }
711 }802 }
712 }803 }
713 }804 }
805
714 >#qr--modal-messageSyntax {806 >#qr--modal-messageSyntax {
715 grid-column: 1;807 grid-column: 1;
716 grid-row: 1;808 grid-row: 1;
@@ -720,10 +812,12 @@
720 overflow: hidden;812 overflow: hidden;
721 min-width: 100%;813 min-width: 100%;
722 width: 0;814 width: 0;
815
723 >#qr--modal-messageSyntaxInner {816 >#qr--modal-messageSyntaxInner {
724 height: 100%;817 height: 100%;
725 }818 }
726 }819 }
820
727 >#qr--modal-message {821 >#qr--modal-message {
728 background-color: transparent;822 background-color: transparent;
729 color: transparent;823 color: transparent;
@@ -731,19 +825,25 @@
731 grid-row: 1;825 grid-row: 1;
732 caret-color: var(--ac-style-color-text);826 caret-color: var(--ac-style-color-text);
733 overflow: auto;827 overflow: auto;
734 &::-webkit-scrollbar, &::-webkit-scrollbar-thumb {828
829 &::-webkit-scrollbar,
830 &::-webkit-scrollbar-thumb {
735 visibility: hidden;831 visibility: hidden;
736 cursor: default;832 cursor: default;
737 }833 }
834
738 &::selection {835 &::selection {
739 color: transparent;836 color: transparent;
740 background-color: rgba(108 171 251 / 0.25);837 background-color: rgba(108 171 251 / 0.25);
838
741 @supports (color: rgb(from white r g b / 0.25)) {839 @supports (color: rgb(from white r g b / 0.25)) {
742 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);840 background-color: rgb(from var(--ac-style-color-matchedText) r g b / 0.25);
743 }841 }
744 }842 }
745 }843 }
746 #qr--modal-message, #qr--modal-messageSyntaxInner {844
845 #qr--modal-message,
846 #qr--modal-messageSyntaxInner {
747 font-family: var(--monoFontFamily);847 font-family: var(--monoFontFamily);
748 padding: 0.75em;848 padding: 0.75em;
749 margin: 0;849 margin: 0;
@@ -756,10 +856,12 @@
756 }856 }
757 }857 }
758 }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 }
924
822 #qr--modal-debugButtons {925 #qr--modal-debugButtons {
823 display: none;926 display: none;
824 gap: 1em;927 gap: 1em;
928
825 .qr--modal-debugButton {929 .qr--modal-debugButton {
826 aspect-ratio: 1.25 / 1;930 aspect-ratio: 1.25 / 1;
827 width: 2.25em;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,18 +945,23 @@
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 }
@@ -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 {
@@ -941,11 +1052,14 @@
941 width: 0;1052 width: 0;
942 white-space: pre-wrap;1053 white-space: pre-wrap;
943 }1054 }
1055
944 #qr--modal-debugState {1056 #qr--modal-debugState {
945 display: none;1057 display: none;
1058
946 &.qr--active {1059 &.qr--active {
947 display: block;1060 display: block;
948 }1061 }
1062
949 text-align: left;1063 text-align: left;
950 font-size: smaller;1064 font-size: smaller;
951 font-family: var(--monoFontFamily);1065 font-family: var(--monoFontFamily);
@@ -961,6 +1075,7 @@
961 display: grid;1075 display: grid;
962 grid-template-columns: 0fr 1fr 1fr;1076 grid-template-columns: 0fr 1fr 1fr;
963 column-gap: 0em;1077 column-gap: 0em;
1078
964 .qr--title {1079 .qr--title {
965 grid-column: 1 / 4;1080 grid-column: 1 / 4;
966 font-weight: bold;1081 font-weight: bold;
@@ -969,47 +1084,66 @@
969 padding: 0.25em;1084 padding: 0.25em;
970 margin-top: 0.5em;1085 margin-top: 0.5em;
971 }1086 }
972 .qr--var, .qr--macro, .qr--pipe {1087
1088 .qr--var,
1089 .qr--macro,
1090 .qr--pipe {
973 display: contents;1091 display: contents;
1092
974 &:nth-child(2n + 1) {1093 &:nth-child(2n + 1) {
975 .qr--key, .qr--val {1094
1095 .qr--key,
1096 .qr--val {
976 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);1097 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.25);
977 }1098 }
1099
978 .qr--val {1100 .qr--val {
979 &:nth-child(2n) {1101 &:nth-child(2n) {
980 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.125);1102 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.125);
981 }1103 }
1104
982 &:hover {1105 &:hover {
983 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);1106 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);
984 }1107 }
985 }1108 }
986 }1109 }
1110
987 &:nth-child(2n) {1111 &:nth-child(2n) {
988 .qr--val {1112 .qr--val {
989 &:nth-child(2n) {1113 &:nth-child(2n) {
990 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.0625);1114 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.0625);
991 }1115 }
1116
992 &:hover {1117 &:hover {
993 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);1118 background-color: rgb(from var(--SmartThemeEmColor) r g b / 0.5);
994 }1119 }
995 }1120 }
996 }1121 }
1122
997 &.qr--isHidden {1123 &.qr--isHidden {
998 .qr--key, .qr--val {1124
1125 .qr--key,
1126 .qr--val {
999 opacity: 0.5;1127 opacity: 0.5;
1000 }1128 }
1001 }1129 }
1130
1002 .qr--val {1131 .qr--val {
1003 grid-column: 2 / 4;1132 grid-column: 2 / 4;
1133
1004 &.qr--singleCol {1134 &.qr--singleCol {
1005 grid-column: unset;1135 grid-column: unset;
1006 }1136 }
1137
1007 &.qr--simple {1138 &.qr--simple {
1008 &:before, &:after {1139
1140 &:before,
1141 &:after {
1009 content: '"';1142 content: '"';
1010 color: var(--SmartThemeQuoteColor);1143 color: var(--SmartThemeQuoteColor);
1011 }1144 }
1012 }1145 }
1146
1013 &.qr--unresolved {1147 &.qr--unresolved {
1014 &:after {1148 &:after {
1015 content: '-UNRESOLVED-';1149 content: '-UNRESOLVED-';
@@ -1019,21 +1153,36 @@
1019 }1153 }
1020 }1154 }
1021 }1155 }
1156
1022 .qr--key {1157 .qr--key {
1023 margin-left: 0.5em;1158 margin-left: 0.5em;
1024 padding-right: 1em;1159 padding-right: 1em;
1025 &:after { content: ": "; }1160
1161 &:after {
1162 content: ": ";
1026 }1163 }
1027 .qr--pipe, .qr--macro {1164 }
1165
1166 .qr--pipe,
1167 .qr--macro {
1028 >.qr--key {1168 >.qr--key {
1029 &:before { content: "{{"; }1169 &:before {
1030 &:after { content: "}}: "; }1170 content: "{{";
1171 }
1172
1173 &:after {
1174 content: "}}: ";
1175 }
1031 }1176 }
1032 }1177 }
1178
1033 .qr--scope {1179 .qr--scope {
1034 display: contents;1180 display: contents;
1181
1035 .qr--pipe {1182 .qr--pipe {
1036 .qr--key, .qr--val {1183
1184 .qr--key,
1185 .qr--val {
1037 opacity: 0.5;1186 opacity: 0.5;
1038 }1187 }
1039 }1188 }
@@ -1043,6 +1192,7 @@
1043 .qr--stack {1192 .qr--stack {
1044 display: grid;1193 display: grid;
1045 grid-template-columns: 1fr 0fr;1194 grid-template-columns: 1fr 0fr;
1195
1046 .qr--title {1196 .qr--title {
1047 grid-column: 1 / 3;1197 grid-column: 1 / 3;
1048 font-weight: bold;1198 font-weight: bold;
@@ -1051,16 +1201,22 @@
1051 padding: 0.25em;1201 padding: 0.25em;
1052 margin-top: 1em;1202 margin-top: 1em;
1053 }1203 }
1204
1054 .qr--item {1205 .qr--item {
1055 display: contents;1206 display: contents;
1207
1056 &:nth-child(2n + 1) {1208 &:nth-child(2n + 1) {
1057 .qr--name, .qr--source {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;
@@ -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);
@@ -1098,6 +1255,7 @@
10981255
1099.popup.qr--hide {1256.popup.qr--hide {
1100 opacity: 0 !important;1257 opacity: 0 !important;
1258
1101 &::backdrop {1259 &::backdrop {
1102 opacity: 0 !important;1260 opacity: 0 !important;
1103 }1261 }
@@ -1117,14 +1275,16 @@
1117 overflow: hidden;1275 overflow: hidden;
1118 font-weight: bold;1276 font-weight: bold;
1119 }1277 }
1278
1120 display: flex;1279 display: flex;
1121 align-items: center;1280 align-items: center;
1122 flex-direction: column;1281 flex-direction: column;
1123 white-space: pre;1282 white-space: pre;
1124 font-weight: normal;1283 font-weight: normal;
1125 box-shadow: 0 0 0;1284 box-shadow: 0 0 0;
1126 transition: 200ms;1285 transition: var(--animation-duration-2x);
1127 }1286 }
1287
1128 .qr--copy {1288 .qr--copy {
1129 &:after {1289 &:after {
1130 content: 'Copy';1290 content: 'Copy';
@@ -1132,24 +1292,28 @@
1132 overflow: hidden;1292 overflow: hidden;
1133 font-weight: bold;1293 font-weight: bold;
1134 }1294 }
1295
1135 display: flex;1296 display: flex;
1136 align-items: center;1297 align-items: center;
1137 flex-direction: column;1298 flex-direction: column;
1138 white-space: pre;1299 white-space: pre;
1139 font-weight: normal;1300 font-weight: normal;
1140 box-shadow: 0 0 0;1301 box-shadow: 0 0 0;
1141 transition: 200ms;1302 transition: var(--animation-duration-2x);
1142 }1303 }
1304
1143 &:has(.qr--transferSelect:focus) {1305 &:has(.qr--transferSelect:focus) {
1144 .popup-button-ok {1306 .popup-button-ok {
1145 font-weight: bold;1307 font-weight: bold;
1146 box-shadow: 0 0 10px;1308 box-shadow: 0 0 10px;
1147 }1309 }
1310
1148 &.qr--isCopy {1311 &.qr--isCopy {
1149 .popup-button-ok {1312 .popup-button-ok {
1150 font-weight: normal;1313 font-weight: normal;
1151 box-shadow: 0 0 0;1314 box-shadow: 0 0 0;
1152 }1315 }
1316
1153 .qr--copy {1317 .qr--copy {
1154 font-weight: bold;1318 font-weight: bold;
1155 box-shadow: 0 0 10px;1319 box-shadow: 0 0 10px;
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+574 -643
@@ -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 });
1959
1960 // Step 2: Clean all <select> elements
1961 $list.find('select').each(function () {
1962 const $select = $(this);
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 }
19441998
1945 // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements1999 console.timeEnd('clearEntryList');
1946 // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it2000}
1947 worldEntriesList.find('*').off();2001
1948 worldEntriesList.empty().show();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
2109 for (const entry of page) {
2110 try {
2111 const block = await getWorldEntry(name, data, entry);
2112 if (block) {
2113 blocks.push(block);
2114 }
2115 } catch (error) {
2116 console.error(`Error while processing entry ${entry.uid}:`, error);
2117 }
2118 }
2119
2049 const isCustomOrder = $('#world_info_sort_order').find(':selected').data('rule') === 'custom';2120 const isCustomOrder = $('#world_info_sort_order').find(':selected').data('rule') === 'custom';
2050 if (!isCustomOrder) {2121 if (!isCustomOrder) {
2051 blocks.forEach(block => {2122 blocks.forEach(block => {
2052 block.find('.drag-handle').remove();2123 block.find('.drag-handle').remove();
2053 });2124 });
2054 }2125 }
2126
2055 worldEntriesList.append(keywordHeaders);2127 worldEntriesList.append(keywordHeaders);
2056 worldEntriesList.append(blocks);2128 worldEntriesList.append(blocks);
2129 } catch (error) {
2130 console.error('Error while rendering WI entries:', error);
2131 } finally {
2132 isSaveWorldInfoDisabled = false;
2133 }
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,51 +2531,32 @@ 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;
2459 }
2460
2461 const template = WI_ENTRY_EDIT_TEMPLATE.clone();
2462 template.data('uid', entry.uid);
2463 template.attr('uid', entry.uid);
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;
2467
2468 /** Function to build the keys input controls @param {string} entryPropName @param {string} originalDataValueName */
2469 function enableKeysInput(entryPropName, originalDataValueName) {
2470 const isFancyInput = !isMobile() && !power_user.wi_key_input_plaintext;2536 const isFancyInput = !isMobile() && !power_user.wi_key_input_plaintext;
2471 const input = isFancyInput ? template.find(`select[name="${entryPropName}"]`) : template.find(`textarea[name="${entryPropName}"]`);2537 const input = isFancyInput ? template.find(`select[name="${entryPropName}"]`) : template.find(`textarea[name="${entryPropName}"]`);
2472 input.data('uid', entry.uid);2538 input.data('uid', entry.uid);
2473 input.on('click', function (event) {2539 input.on('click', function (event) {
2474 // Prevent closing the drawer on clicking the input
2475 event.stopPropagation();2540 event.stopPropagation();
2476 });2541 });
24772542
2478 function templateStyling(/** @type {Select2Option} */ item, { searchStyle = false } = {}) {2543 function templateStyling(item, { searchStyle = false } = {}) {
2479 const content = $('<span>').addClass('item').text(item.text).attr('title', `${item.text}\n\nClick to edit`);2544 const content = $('<span>').addClass('item').text(item.text).attr('title', `${item.text}\n\nClick to edit`);
2480 const isRegex = isValidRegex(item.text);2545 const isRegex = isValidRegex(item.text);
2481 if (isRegex) {2546 if (isRegex) {
2482 content.html(highlightRegex(item.text));2547 content.html(highlightRegex(item.text));
2483 content.addClass('regex_item').prepend($('<span>').addClass('regex_icon').text('•*').attr('title', 'Regex'));2548 content.addClass('regex_item').prepend($('<span>').addClass('regex_icon').text('•*').attr('title', 'Regex'));
2484 }2549 }
2485
2486 if (searchStyle && item.count) {2550 if (searchStyle && item.count) {
2487 // Build a wrapping element2551 const wrapper = $('<span>').addClass('result_block').append(content);
2488 const wrapper = $('<span>').addClass('result_block')
2489 .append(content);
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`));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`));
2491 return wrapper;2553 return wrapper;
2492 }2554 }
2493
2494 return content;2555 return content;
2495 }2556 }
24962557
2497 if (isFancyInput) {2558 if (isFancyInput) {
2498 // First initialize existing values as options, before initializing select2, to speed up performance
2499 select2ModifyOptions(input, entry[entryPropName], { select: true, changeEventArgs: { skipReset: true, noSave: true } });2559 select2ModifyOptions(input, entry[entryPropName], { select: true, changeEventArgs: { skipReset: true, noSave: true } });
2500
2501 input.select2({2560 input.select2({
2502 ajax: dynamicSelect2DataViaAjax(() => worldEntryKeyOptionsCache),2561 ajax: dynamicSelect2DataViaAjax(() => worldEntryKeyOptionsCache),
2503 tags: true,2562 tags: true,
@@ -2507,12 +2566,18 @@ export async function getWorldEntry(name, data, entry) {
2507 templateResult: item => templateStyling(item, { searchStyle: true }),2566 templateResult: item => templateStyling(item, { searchStyle: true }),
2508 templateSelection: item => templateStyling(item),2567 templateSelection: item => templateStyling(item),
2509 });2568 });
2510 input.on('change', async function (_, { skipReset, noSave } = {}) {2569
2570 // TypeScript-safe event handler
2571 /**
2572 * @param {Event} _event
2573 * @param {{ skipReset?: boolean, noSave?: boolean }} [arg]
2574 */
2575 input.on('change', async function (_event, arg) {
2511 const uid = $(this).data('uid');2576 const uid = $(this).data('uid');
2512 /** @type {string[]} */
2513 const keys = ($(this).select2('data')).map(x => x.text);2577 const keys = ($(this).select2('data')).map(x => x.text);
25142578 const skipReset = arg?.skipReset ?? false;
2515 !skipReset && await resetScrollHeight(this);2579 const noSave = arg?.noSave ?? false;
2580 if (!skipReset) await resetScrollHeight(this);
2516 if (!noSave) {2581 if (!noSave) {
2517 data.entries[uid][entryPropName] = keys;2582 data.entries[uid][entryPropName] = keys;
2518 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);2583 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
@@ -2520,37 +2585,34 @@ export async function getWorldEntry(name, data, entry) {
2520 }2585 }
2521 $(this).toggleClass('empty', !data.entries[uid][entryPropName].length);2586 $(this).toggleClass('empty', !data.entries[uid][entryPropName].length);
2522 });2587 });
2588
2523 input.toggleClass('empty', !entry[entryPropName].length);2589 input.toggleClass('empty', !entry[entryPropName].length);
2524 input.on('select2:select', /** @type {function(*):void} */ event => updateWorldEntryKeyOptionsCache([event.params.data]));2590 input.on('select2:select', event => updateWorldEntryKeyOptionsCache([event.params.data]));
2525 input.on('select2:unselect', /** @type {function(*):void} */ event => updateWorldEntryKeyOptionsCache([event.params.data], { remove: true }));2591 input.on('select2:unselect', event => updateWorldEntryKeyOptionsCache([event.params.data], { remove: true }));
25262592
2527 select2ChoiceClickSubscribe(input, target => {2593 select2ChoiceClickSubscribe(input, target => {
2528 const key = $(target.closest('.regex-highlight, .item')).text();2594 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();2595 const selected = input.val();
2533 if (!Array.isArray(selected)) return;2596 if (!Array.isArray(selected)) return;
2534 var index = selected.indexOf(getSelect2OptionId(key));2597 var index = selected.indexOf(getSelect2OptionId(key));
2535 if (index > -1) selected.splice(index, 1);2598 if (index > -1) selected.splice(index, 1);
2536 input.val(selected).trigger('change');2599 input.val(selected).trigger('change');
2537 // Manually update the cache, that change event is not gonna trigger it
2538 updateWorldEntryKeyOptionsCache([key], { remove: true });2600 updateWorldEntryKeyOptionsCache([key], { remove: true });
25392601 input.next('span.select2-container').find('textarea').val(key).trigger('input');
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 });2602 }, { openDrawer: true });
2544 }2603 } else {
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();2604 template.find(`select[name="${entryPropName}"]`).hide();
2548 input.show();2605 input.show();
25492606 /**
2550 input.on('input', async function (_, { skipReset, noSave } = {}) {2607 * @param {Event} _event
2608 * @param {{ skipReset?: boolean, noSave?: boolean }} [arg]
2609 */
2610 input.on('change', async function (_event, arg) {
2551 const uid = $(this).data('uid');2611 const uid = $(this).data('uid');
2552 const value = String($(this).val());2612 const value = String($(this).val());
2553 !skipReset && await resetScrollHeight(this);2613 const skipReset = arg?.skipReset ?? false;
2614 const noSave = arg?.noSave ?? false;
2615 if (!skipReset) await resetScrollHeight(this);
2554 if (!noSave) {2616 if (!noSave) {
2555 data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value);2617 data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value);
2556 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);2618 setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]);
@@ -2563,98 +2625,43 @@ export async function getWorldEntry(name, data, entry) {
2563 return { isFancy: isFancyInput, control: input };2625 return { isFancy: isFancyInput, control: input };
2564}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');2636 const uid = $(this).data('uid');
2599 const value = Number($(this).val());2637 const value = $(this).prop('checked');
2600 data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY;2638 data.entries[uid][fieldName] = value;
2601 setWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic);2639 setWIOriginalDataValue(data, uid, key, data.entries[uid][fieldName]);
2602 await saveWorldInfo(name, data);2640 await saveWorldInfo(name, data);
2603 });2641 });
26042642 checkBoxElem.prop('checked', !!entry[fieldName]).trigger('input');
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');
2619 const value = $(this).prop('checked');
2620 characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)');
2621 if (data.entries[uid].characterFilter) {
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}2643}
26392644
2640 // Verify names to exist in the system2645/**
2641 if (data.entries[uid]?.characterFilter?.names?.length > 0) {2646 * Helper to update position/order display.
2642 for (const name of [...data.entries[uid].characterFilter.names]) {2647 */
2643 if (!getContext().characters.find(x => x.avatar.replace(/\.[^/.]+$/, '') === name)) {2648function updatePosOrdDisplayHelper({ template, data, uid }) {
2644 console.warn(`World Info: Character ${name} not found. Removing from the entry filter.`, entry);2649 let entry = data.entries[uid];
2645 data.entries[uid].characterFilter.names = data.entries[uid].characterFilter.names.filter(x => x !== name);2650 let posText = entry.position;
2646 }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;
2647 }2657 }
2658 template.find('.world_entry_form_position_value').text(`(${posText} ${entry.order})`);
2648}2659}
26492660
2650 setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter);2661/**
2651 await saveWorldInfo(name, data);2662 * Helper to initialize character filter select2.
2652 });2663 */
2653 characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input');2664function initCharacterFilterSelect2Helper(characterFilter, t) {
2654
2655 const characterFilter = template.find('select[name="characterFilter"]');
2656 characterFilter.data('uid', entry.uid);
2657
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 });
2715
2716 // comment
2717 const commentInput = template.find('textarea[name="comment"]');
2718 const commentToggle = template.find('input[name="addMemo"]');
2719 commentInput.data('uid', entry.uid);
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}2729}
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');
28012730
2802 const keysecondarytextpole = $(this)2731/**
2803 .closest('.world_entry')2732 * Helper to handle probability input.
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 */2733 */
28342734function handleProbabilityInputHelper({ probabilityInput, data, entry, name, setWIOriginalDataValue, saveWorldInfo }) {
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)');
2987
2988 // probability toggle
2989 if (entry.useProbability === undefined) {
2990 entry.useProbability = false;
2991}2751}
29922752
2993 const probabilityToggle = template.find('input[name="useProbability"]');2753/**
2754 * Helper to handle probability toggle.
2755 */
2756function handleProbabilityToggleHelper({ probabilityToggle, data, entry, name, probabilityInput, setWIOriginalDataValue, saveWorldInfo }) {
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();
3018
3019 // position
3020 if (entry.position === undefined) {
3021 entry.position = 0;
3022}2775}
30232776
3024 const positionInput = template.find('select[name="position"]');2777/**
3025 //initScrollHeight(positionInput);2778 * Helper to handle select2 dropdowns for boolean selects.
3026 positionInput.data('uid', entry.uid);2779 */
3027 positionInput.on('click', function (event) {2780function handleBooleanSelectHelper({ selectElem, entry, entryKey, data, name, setWIOriginalDataValue, saveWorldInfo }) {
3028 // Prevent closing the drawer on clicking the input2781 selectElem.data('uid', entry.uid);
3029 event.stopPropagation();2782 selectElem.on('input', async function () {
2783 const uid = $(this).data('uid');
2784 const value = $(this).val();
2785 data.entries[uid][entryKey] = value === 'null' ? null : value === 'true';
2786 setWIOriginalDataValue(data, uid, `extensions.${entryKey.replace(/[A-Z]/g, m => `_${m.toLowerCase()}`)}`, data.entries[uid][entryKey]);
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 {
3042 depthInput.prop('disabled', true);
3043 depthInput.css('visibility', 'hidden');
3044 data.entries[uid].role = null;
3045 //depthInput.parent().hide();
3046 }2807 }
3047 updatePosOrdDisplay(uid);2808 }
3048 // Spec v2 only supports before_char and after_char2809 data.entries[uid][entryKey] = !isNaN(value) ? value : null;
3049 setWIOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char');2810 setWIOriginalDataValue(data, uid, `extensions.${entryKey.replace(/[A-Z]/g, m => `_${m.toLowerCase()}`)}`, data.entries[uid][entryKey]);
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');
31662912
3167 // If the value contains a number, we'll take that one (set by the level input), otherwise we can use true/false switch2913 // Probability
3168 const value = toggled ? data.entries[uid].delayUntilRecursion || true : false;2914 handleProbabilityInputHelper({ probabilityInput: headerTemplate.find('input[name="probability"]'), data, entry, name, setWIOriginalDataValue, saveWorldInfo });
31692915
3170 if (!toggled) delayUntilRecursionLevelInput.val('');2916 // Depth
2917 handleNumberInputHelper({
2918 inputElem: headerTemplate.find('input[name="depth"]'),
2919 entry, entryKey: 'depth', data, name, setWIOriginalDataValue, saveWorldInfo,
2920 min: 0, max: MAX_SCAN_DEPTH, clamp: false,
2921 });
2922 headerTemplate.find('input[name="depth"]').css('width', 'calc(3em + 15px)');
31712923
3172 data.entries[uid].delayUntilRecursion = value;2924 // Position
3173 setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);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 () {
2930 const uid = $(this).data('uid');
2931 const value = Number($(this).val());
2932 data.entries[uid].position = !isNaN(value) ? value : 0;
2933 const depthInput = headerTemplate.find('input[name="depth"]');
2934 if (value === world_info_position.atDepth) {
2935 depthInput.prop('disabled', false);
2936 depthInput.css('visibility', 'visible');
2937 const role = Number($(this).find(':selected').data('role'));
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);
3174 await saveWorldInfo(name, data);2948 await saveWorldInfo(name, data);
3175 });2949 });
3176 delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');2950 const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : '';
3177 delayUntilRecursionLevelInput.on('input', async function () {2951 headerTemplate.find(`select[name="position"] option[value="${entry.position}"][data-role="${roleValue}"]`).prop('selected', true).trigger('input');
3178 const uid = $(this).data('uid');
3179 const content = $(this).val();
3180 const value = content === '' ? (typeof data.entries[uid].delayUntilRecursion === 'boolean' ? data.entries[uid].delayUntilRecursion : true)
3181 : content === 1 ? true
3182 : !isNaN(Number(content)) ? Number(content)
3183 : false;
31842952
3185 data.entries[uid].delayUntilRecursion = value;2953 // Tri-state selector
3186 setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion);2954 handleEntryStateSelectorHelper({
3187 await saveWorldInfo(name, data);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,
3188 });2963 });
3189 // No need to retrigger inpout event, we'll just set the curret current value. It was edited/saved above already
3190 delayUntilRecursionLevelInput.val(['number', 'string'].includes(typeof entry.delayUntilRecursion) ? entry.delayUntilRecursion : '').trigger('input');
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,143 +3005,327 @@ 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;
3026 if (selectedWorldIndex === -1) return;
3027 const selectedValue = world_names[selectedWorldIndex];
3028 if (!selectedValue) {
3029 toastr.warning(t`Please select a target lorebook.`);
3278 return;3030 return;
3279 }3031 }
3032 await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid);
3033 });
32803034
3281 if (selectedWorldIndex === -1) {3035 let drawerInitialized = false;
3036 let drawerDestroyTimeout = null;
3037 headerTemplate.find('.inline-drawer').on('inline-drawer-toggle', function () {
3038 if (drawerDestroyTimeout) {
3039 clearTimeout(drawerDestroyTimeout);
3040 drawerDestroyTimeout = null;
3041 }
3042 if (drawerInitialized) {
3043 drawerDestroyTimeout = setTimeout(() => {
3044 // Drawer was reopened, so we don't destroy it
3045 if (editOutlet.is(':visible')) {
3282 return;3046 return;
3283 }3047 }
3048 drawerInitialized = false;
3049 clearEntryList(editOutlet);
3050 drawerDestroyTimeout = null;
3051 }, debounce_timeout.relaxed);
3052 } else {
3053 drawerInitialized = true;
3054 addEditorDrawerContent();
3055 }
3056 });
32843057
3285 const selectedValue = world_names[selectedWorldIndex];3058 const editOutlet = headerTemplate.find('.inline-drawer-outlet');
32863059
3287 if (!selectedValue) {3060 function addEditorDrawerContent() {
3288 toastr.warning(t`Please select a target lorebook.`);3061 const editTemplate = WI_ENTRY_EDIT_TEMPLATE.clone();
3289 return;3062
3063 // UID display
3064 editTemplate.find('.world_entry_form_uid_value').text(`(UID: ${entry.uid})`);
3065
3066 // Key inputs
3067 const keyInput = enableKeysInputHelper({ template: editTemplate, entry, entryPropName: 'key', originalDataValueName: 'keys', name, data });
3068 const keySecondaryInput = enableKeysInputHelper({ template: editTemplate, entry, entryPropName: 'keysecondary', originalDataValueName: 'secondary_keys', name, data });
3069 if (!keyInput.isFancy) initScrollHeight(keyInput.control);
3070 if (!keySecondaryInput.isFancy) initScrollHeight(keySecondaryInput.control);
3071
3072 // Key input switch
3073 editTemplate.find('.switch_input_type_icon').on('click', function () {
3074 power_user.wi_key_input_plaintext = !power_user.wi_key_input_plaintext;
3075 saveSettingsDebounced();
3076 const uid = ($(this).parents('.world_entry')).data('uid');
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 });
3083
3084 // Probability toggle
3085 handleProbabilityToggleHelper({
3086 probabilityToggle: editTemplate.find('input[name="useProbability"]'),
3087 data, entry, name,
3088 probabilityInput: headerTemplate.find('input[name="probability"]'),
3089 setWIOriginalDataValue, saveWorldInfo,
3090 });
3091
3092 // Comment toggle
3093 const commentToggle = editTemplate.find('input[name="addMemo"]');
3094 commentToggle.data('uid', entry.uid);
3095 commentToggle.on('input', async function () {
3096 const uid = $(this).data('uid');
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');
3118
3119 // Selective
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);
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;
3290 }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');
32913167
3292 await moveWorldInfoEntry(sourceWorld, selectedValue, sourceUid);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,
3174 });
3175
3176 // Content
3177 const counter = editTemplate.find('.world_entry_form_token_counter');
3178 const countTokensDebounced = debounce(async function (counter, value) {
3179 const numberOfTokens = await getTokenCountAsync(value);
3180 $(counter).text(numberOfTokens);
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);
3293 });3193 });
3194 contentInput.val(entry.content).trigger('input', { skipCount: true });
3195 editTemplate.find('.editor_maximize').attr('data-for', contentInputId);
32943196
3295 // scan depth3197 // Scan depth
3296 const scanDepthInput = template.find('input[name="scanDepth"]');3198 const scanDepthInput = editTemplate.find('input[name="scanDepth"]');
3297 scanDepthInput.data('uid', entry.uid);3199 scanDepthInput.data('uid', entry.uid);
3298 scanDepthInput.on('input', async function () {3200 scanDepthInput.on('input', async function () {
3299 const uid = $(this).data('uid');3201 const uid = $(this).data('uid');
3300 const isEmpty = $(this).val() === '';3202 const isEmpty = $(this).val() === '';
3301 const value = Number($(this).val());3203 const value = Number($(this).val());
3302
3303 // Clamp if necessary
3304 if (value < 0) {3204 if (value < 0) {
3305 $(this).val(0).trigger('input');3205 $(this).val(0).trigger('input');
3306 toastr.warning('Scan depth cannot be negative');3206 toastr.warning('Scan depth cannot be negative');
3307 return;3207 return;
3308 }3208 }
3309
3310 if (value > MAX_SCAN_DEPTH) {3209 if (value > MAX_SCAN_DEPTH) {
3311 $(this).val(MAX_SCAN_DEPTH).trigger('input');3210 $(this).val(MAX_SCAN_DEPTH).trigger('input');
3312 toastr.warning(`Scan depth cannot exceed ${MAX_SCAN_DEPTH}`);3211 toastr.warning(`Scan depth cannot exceed ${MAX_SCAN_DEPTH}`);
3313 return;3212 return;
3314 }3213 }
3315
3316 data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null;3214 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);3215 setWIOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth);
3318 await saveWorldInfo(name, data);3216 await saveWorldInfo(name, data);
3319 });3217 });
3320 scanDepthInput.val(entry.scanDepth ?? null).trigger('input');3218 scanDepthInput.val(entry.scanDepth ?? null).trigger('input');
33213219
3322 // case sensitive select3220 // Group
3323 const caseSensitiveSelect = template.find('select[name="caseSensitive"]');3221 const groupInput = editTemplate.find('input[name="group"]');
3324 caseSensitiveSelect.data('uid', entry.uid);3222 groupInput.data('uid', entry.uid);
3325 caseSensitiveSelect.on('input', async function () {3223 groupInput.on('input', async function () {
3326 const uid = $(this).data('uid');3224 const uid = $(this).data('uid');
3327 const value = $(this).val();3225 const value = String($(this).val()).trim();
33283226 data.entries[uid].group = value;
3329 data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true';3227 setWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group);
3330 setWIOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive);
3331 await saveWorldInfo(name, data);3228 await saveWorldInfo(name, data);
3332 });3229 });
3333 caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input');3230 groupInput.val(entry.group ?? '').trigger('input');
3231 setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1);
33343232
3335 // match whole words select3233 // Inclusion priority
3336 const matchWholeWordsSelect = template.find('select[name="matchWholeWords"]');3234 const groupOverrideInput = editTemplate.find('input[name="groupOverride"]');
3337 matchWholeWordsSelect.data('uid', entry.uid);3235 groupOverrideInput.data('uid', entry.uid);
3338 matchWholeWordsSelect.on('input', async function () {3236 groupOverrideInput.on('input', async function () {
3339 const uid = $(this).data('uid');3237 const uid = $(this).data('uid');
3340 const value = $(this).val();3238 const value = $(this).prop('checked');
33413239 data.entries[uid].groupOverride = value;
3342 data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true';3240 setWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride);
3343 setWIOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords);
3344 await saveWorldInfo(name, data);3241 await saveWorldInfo(name, data);
3345 });3242 });
3346 matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input');3243 groupOverrideInput.prop('checked', entry.groupOverride).trigger('input');
33473244
3348 // use group scoring select3245 // Group weight
3349 const useGroupScoringSelect = template.find('select[name="useGroupScoring"]');3246 handleNumberInputHelper({
3350 useGroupScoringSelect.data('uid', entry.uid);3247 inputElem: editTemplate.find('input[name="groupWeight"]'),
3351 useGroupScoringSelect.on('input', async function () {3248 entry, entryKey: 'groupWeight', data, name, setWIOriginalDataValue, saveWorldInfo,
3352 const uid = $(this).data('uid');3249 min: 1, max: 10000, clamp: true,
3353 const value = $(this).val();3250 });
33543251
3355 data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true';3252 // Sticky, cooldown, delay
3356 setWIOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring);3253 handleNumberInputHelper({
3357 await saveWorldInfo(name, data);3254 inputElem: editTemplate.find('input[name="sticky"]'),
3255 entry, entryKey: 'sticky', data, name, setWIOriginalDataValue, saveWorldInfo,
3256 min: 1, max: 10000, clamp: false,
3257 });
3258 handleNumberInputHelper({
3259 inputElem: editTemplate.find('input[name="cooldown"]'),
3260 entry, entryKey: 'cooldown', data, name, setWIOriginalDataValue, saveWorldInfo,
3261 min: 1, max: 10000, clamp: false,
3262 });
3263 handleNumberInputHelper({
3264 inputElem: editTemplate.find('input[name="delay"]'),
3265 entry, entryKey: 'delay', data, name, setWIOriginalDataValue, saveWorldInfo,
3266 min: 1, max: 10000, clamp: false,
3358 });3267 });
3359 useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input');
33603268
3361 function handleMatchCheckbox(fieldName) {3269 // Exclude/prevent recursion
3362 const key = originalWIDataKeyMap[fieldName];3270 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'excludeRecursion', data, name });
3363 const checkBoxElem = template.find(`input[type="checkbox"][name="${fieldName}"]`);3271 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'preventRecursion', data, name });
3364 checkBoxElem.data('uid', entry.uid);
3365 checkBoxElem.on('input', async function () {
3366 const uid = $(this).data('uid');
3367 const value = $(this).prop('checked');
33683272
3369 data.entries[uid][fieldName] = value;3273 // Delay until recursion
3370 setWIOriginalDataValue(data, uid, key, data.entries[uid][fieldName]);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);
3371 await saveWorldInfo(name, data);3285 await saveWorldInfo(name, data);
3372 });3286 });
3373 checkBoxElem.prop('checked', !!entry[fieldName]).trigger('input');3287 delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input');
3374 }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 });
33753314
3376 handleMatchCheckbox('matchPersonaDescription');3315 // Match checkboxes
3377 handleMatchCheckbox('matchCharacterDescription');3316 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchPersonaDescription', data, name });
3378 handleMatchCheckbox('matchCharacterPersonality');3317 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterDescription', data, name });
3379 handleMatchCheckbox('matchCharacterDepthPrompt');3318 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterPersonality', data, name });
3380 handleMatchCheckbox('matchScenario');3319 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCharacterDepthPrompt', data, name });
3381 handleMatchCheckbox('matchCreatorNotes');3320 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchScenario', data, name });
3321 handleMatchCheckboxHelper({ template: editTemplate, entry, fieldName: 'matchCreatorNotes', data, name });
33823322
3383 // automation id3323 // Automation ID
3384 const automationIdInput = template.find('input[name="automationId"]');3324 const automationIdInput = editTemplate.find('input[name="automationId"]');
3385 automationIdInput.data('uid', entry.uid);3325 automationIdInput.data('uid', entry.uid);
3386 automationIdInput.on('input', async function () {3326 automationIdInput.on('input', async function () {
3387 const uid = $(this).data('uid');3327 const uid = $(this).data('uid');
3388 const value = $(this).val();3328 const value = $(this).val();
3389
3390 data.entries[uid].automationId = value;3329 data.entries[uid].automationId = value;
3391 setWIOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId);3330 setWIOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId);
3392 await saveWorldInfo(name, data);3331 await saveWorldInfo(name, data);
@@ -3394,33 +3333,15 @@ export async function getWorldEntry(name, data, entry) {
3394 automationIdInput.val(entry.automationId ?? '').trigger('input');3333 automationIdInput.val(entry.automationId ?? '').trigger('input');
3395 setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1);3334 setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1);
33963335
3397 template.find('.inline-drawer-content').css('display', 'none'); //entries start collapsed3336 countTokensDebounced(counter, contentInput.val());
33983337
3399 function updatePosOrdDisplay(uid) {3338 editTemplate.find('.inline-drawer-content').css('display', 'none');
3400 // display position/order info left of keyword box3339 editOutlet.append(editTemplate);
3401 let entry = data.entries[uid];
3402 let posText = entry.position;
3403 switch (entry.position) {
3404 case 0:
3405 posText = '↑CD';
3406 break;
3407 case 1:
3408 posText = 'CD↓';
3409 break;
3410 case 2:
3411 posText = '↑AN';
3412 break;
3413 case 3:
3414 posText = 'AN↓';
3415 break;
3416 case 4:
3417 posText = `@D${entry.depth}`;
3418 break;
3419 }
3420 template.find('.world_entry_form_position_value').text(`(${posText} ${entry.order})`);
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 {