Merge branch 'staging' into parser-v2

This commit is contained in:
LenAnderson
2024-04-08 19:18:21 -04:00
8 changed files with 186 additions and 30 deletions

View File

@ -58,6 +58,11 @@
cursor: unset;
}
#rm_group_buttons textarea {
margin: 0px;
min-width: 200px;
}
#rm_group_members,
#rm_group_add_members {
margin-top: 0.25rem;

View File

@ -4368,24 +4368,44 @@
</div>
<div name="GroupStragegyAndOrder" id="rm_group_buttons" class="flex-container paddingLeftRight5 flex2">
<div class="flex1 flexGap5">
<div class="flex-container flexnowrap width100p whitespacenowrap">
<label for="rm_group_activation_strategy" class="flexnowrap width100p whitespacenowrap">
<span data-i18n="Group reply strategy">Group reply strategy</span>
</div>
</label>
<select id="rm_group_activation_strategy">
<option value="0" data-i18n="Natural order">Natural order</option>
<option value="1" data-i18n="List order">List order</option>
</select>
</div>
<div class="flex1 flexGap5">
<div class="flex-container flexnowrap width100p whitespacenowrap">
<label for="rm_group_generation_mode" class="flexnowrap width100p whitespacenowrap">
<span data-i18n="Group generation handling mode">Group generation handling mode</span>
</div>
</label>
<select id="rm_group_generation_mode">
<option value="0" data-i18n="Swap character cards">Swap character cards</option>
<option value="1" data-i18n="Join character cards (exclude muted)">Join character cards (exclude muted)</option>
<option value="2" data-i18n="Join character cards (include muted)">Join character cards (include muted)</option>
</select>
</div>
<div class="flex1 flexGap5" title="Inserted before each part of the joined fields.">
<label for="rm_group_generation_mode_join_prefix" class="flexnowrap width100p whitespacenowrap">
<span data-i18n="Join Prefix">Join Prefix</span>
<div class="fa-solid fa-circle-info opacity50p"
data-i18n="[title]When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)"
title="When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)">
</div>
</label>
<textarea id="rm_group_generation_mode_join_prefix" class="text_pole wide100p textarea_compact autoSetHeight" maxlength="2000" placeholder="&mdash;" rows="1"></textarea>
</div>
<div class="flex1 flexGap5" title="Inserted after each part of the joined fields.">
<label for="rm_group_generation_mode_join_suffix" class="flexnowrap width100p whitespacenowrap">
<span data-i18n="Join Suffix">Join Suffix</span>
<div class="fa-solid fa-circle-info opacity50p"
data-i18n="[title]When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)"
title="When 'Join character cards' is selected, all respective fields of the characters are being joined together.&#13;This means that in the story string for example all character descriptions will be joined to one big text.&#13;If you want those fields to be separated, you can define a prefix or suffix here.&#13;&#13;This value supports normal macros and will also replace {{char}} with the relevant char's name and &lt;FIELDNAME&gt; with the name of the part (e.g.: description, personality, scenario, etc.)">
</div>
</label>
<textarea id="rm_group_generation_mode_join_suffix" class="text_pole wide100p textarea_compact autoSetHeight" maxlength="2000" placeholder="&mdash;" rows="1"></textarea>
</div>
</div>
<div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">
<div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-left-long"></div>

View File

@ -103,7 +103,8 @@ function downloadAssetsList(url) {
if (assetType == 'extension') {
assetTypeMenu.append(`
<div class="assets-list-git">
To download extensions from this page, you need to have <a href="https://git-scm.com/downloads" target="_blank">Git</a> installed.
To download extensions from this page, you need to have <a href="https://git-scm.com/downloads" target="_blank">Git</a> installed.<br>
Click the <i class="fa-solid fa-sm fa-arrow-up-right-from-square"></i> icon to visit the Extension's repo for tips on how to use it.
</div>`);
}
@ -187,7 +188,7 @@ function downloadAssetsList(url) {
.append(`<div class="flex-container flexFlowColumn flexNoGap">
<span class="asset-name flex-container alignitemscenter">
<b>${displayName}</b>
<a class="asset_preview" href="${url}" target="_blank" title="Preview in browser">
<a class="asset_preview" href="${url}" target="_blank" title="Extension repo/guide: ${url}">
<i class="fa-solid fa-sm ${previewIcon}"></i>
</a>
</span>

View File

@ -16,12 +16,20 @@
<label for="qr--modal-message">
Message / Command:
</label>
<small>
<div class="qr--modal-editorSettings">
<label class="checkbox_label">
<input type="checkbox" id="qr--modal-wrap">
<span>Word wrap</span>
</label>
</small>
<label class="checkbox_label">
<span>Tab size:</span>
<input type="number" min="1" max="9" id="qr--modal-tabSize" class="text_pole">
</label>
<label class="checkbox_label">
<input type="checkbox" id="qr--modal-executeShortcut">
<span>Ctrl+Enter to execute</span>
</label>
</div>
<textarea class="monospace" id="qr--modal-message"></textarea>
</div>
</div>

View File

@ -46,6 +46,11 @@ export class QuickReply {
/**@type {HTMLInputElement}*/ settingsDomLabel;
/**@type {HTMLTextAreaElement}*/ settingsDomMessage;
/**@type {HTMLElement}*/ editorExecuteBtn;
/**@type {HTMLElement}*/ editorExecuteErrors;
/**@type {HTMLInputElement}*/ editorExecuteHide;
/**@type {Promise}*/ editorExecutePromise;
get hasContext() {
return this.contextList && this.contextList.length > 0;
@ -211,7 +216,7 @@ export class QuickReply {
});
/**@type {HTMLInputElement}*/
const wrap = dom.querySelector('#qr--modal-wrap');
wrap.checked = JSON.parse(localStorage.getItem('qr--wrap'));
wrap.checked = JSON.parse(localStorage.getItem('qr--wrap') ?? 'false');
wrap.addEventListener('click', () => {
localStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));
updateWrap();
@ -223,9 +228,26 @@ export class QuickReply {
message.style.whiteSpace = 'pre';
}
};
/**@type {HTMLInputElement}*/
const tabSize = dom.querySelector('#qr--modal-tabSize');
tabSize.value = JSON.parse(localStorage.getItem('qr--tabSize') ?? '4');
const updateTabSize = () => {
message.style.tabSize = tabSize.value;
};
tabSize.addEventListener('change', () => {
localStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));
updateTabSize();
});
/**@type {HTMLInputElement}*/
const executeShortcut = dom.querySelector('#qr--modal-executeShortcut');
executeShortcut.checked = JSON.parse(localStorage.getItem('qr--executeShortcut') ?? 'true');
executeShortcut.addEventListener('click', () => {
localStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));
});
/**@type {HTMLTextAreaElement}*/
const message = dom.querySelector('#qr--modal-message');
updateWrap();
updateTabSize();
message.value = this.message;
message.addEventListener('input', () => {
this.updateMessage(message.value);
@ -260,6 +282,12 @@ export class QuickReply {
message.selectionStart = start - 1;
message.selectionEnd = end - count;
this.updateMessage(message.value);
} else if (evt.key == 'Enter' && evt.ctrlKey && !evt.shiftKey && !evt.altKey) {
evt.stopPropagation();
evt.preventDefault();
if (executeShortcut.checked) {
this.executeFromEditor();
}
}
});
@ -388,27 +416,15 @@ export class QuickReply {
/**@type {HTMLElement}*/
const executeErrors = dom.querySelector('#qr--modal-executeErrors');
this.editorExecuteErrors = executeErrors;
/**@type {HTMLInputElement}*/
const executeHide = dom.querySelector('#qr--modal-executeHide');
let executePromise;
this.editorExecuteHide = executeHide;
/**@type {HTMLElement}*/
const executeBtn = dom.querySelector('#qr--modal-execute');
this.editorExecuteBtn = executeBtn;
executeBtn.addEventListener('click', async()=>{
if (executePromise) return;
executeBtn.classList.add('qr--busy');
executeErrors.innerHTML = '';
if (executeHide.checked) {
document.querySelector('#shadow_popup').classList.add('qr--hide');
}
try {
executePromise = this.execute();
await executePromise;
} catch (ex) {
executeErrors.textContent = ex.message;
}
executePromise = null;
executeBtn.classList.remove('qr--busy');
document.querySelector('#shadow_popup').classList.remove('qr--hide');
await this.executeFromEditor();
});
await popupResult;
@ -417,6 +433,24 @@ export class QuickReply {
}
}
async executeFromEditor() {
if (this.editorExecutePromise) return;
this.editorExecuteBtn.classList.add('qr--busy');
this.editorExecuteErrors.innerHTML = '';
if (this.editorExecuteHide.checked) {
document.querySelector('#shadow_popup').classList.add('qr--hide');
}
try {
this.editorExecutePromise = this.execute();
await this.editorExecutePromise;
} catch (ex) {
this.editorExecuteErrors.textContent = ex.message;
}
this.editorExecutePromise = null;
this.editorExecuteBtn.classList.remove('qr--busy');
document.querySelector('#shadow_popup').classList.remove('qr--hide');
}

View File

@ -269,6 +269,20 @@
display: flex;
flex-direction: column;
}
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings {
display: flex;
flex-direction: row;
gap: 1em;
color: var(--grey70);
font-size: smaller;
align-items: baseline;
}
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings > .checkbox_label {
white-space: nowrap;
}
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > .qr--modal-editorSettings > .checkbox_label > input {
font-size: inherit;
}
#dialogue_popup:has(#qr--modalEditor) #dialogue_popup_text > #qr--modalEditor > #qr--main > .qr--modal-messageContainer > #qr--modal-message {
flex: 1 1 auto;
}

View File

@ -293,6 +293,20 @@
flex: 1 1 auto;
display: flex;
flex-direction: column;
> .qr--modal-editorSettings {
display: flex;
flex-direction: row;
gap: 1em;
color: var(--grey70);
font-size: smaller;
align-items: baseline;
> .checkbox_label {
white-space: nowrap;
> input {
font-size: inherit;
}
}
}
> #qr--modal-message {
flex: 1 1 auto;
}

View File

@ -9,9 +9,11 @@ import {
saveBase64AsFile,
PAGINATION_TEMPLATE,
getBase64Async,
resetScrollHeight,
initScrollHeight,
} from './utils.js';
import { RA_CountCharTokens, humanizedDateTime, dragElement, favsToHotswap, getMessageTimeStamp } from './RossAscends-mods.js';
import { loadMovingUIState, sortEntitiesList } from './power-user.js';
import { power_user, loadMovingUIState, sortEntitiesList } from './power-user.js';
import {
chat,
@ -351,6 +353,30 @@ export function getGroupCharacterCards(groupId, characterId) {
return null;
}
/** Runs the macro engine on a text, with custom <FIELDNAME> replace @param {string} value @param {string} characterName @param {string} fieldName @returns {string} */
function customBaseChatReplace(value, fieldName, characterName) {
// We should do the custom field name replacement first, and then run it through the normal macro engine with provided names
value = value.replace(/<FIELDNAME>/gi, fieldName);
return baseChatReplace(value.trim(), name1, characterName);
}
/** Prepares text with prefix/suffix for a character field @param {string} value @param {string} characterName @param {string} fieldName @returns {string} */
function replaceAndPrepareForJoin(value, characterName, fieldName) {
value = value.trim();
if (!value) {
return '';
}
// Prepare and replace prefixes
const prefix = customBaseChatReplace(group.generation_mode_join_prefix, fieldName, characterName);
const suffix = customBaseChatReplace(group.generation_mode_join_suffix, fieldName, characterName);
const separator = power_user.instruct.wrap ? '\n' : '';
// Also run the macro replacement on the actual content
value = customBaseChatReplace(value, fieldName, characterName);
return `${prefix ? prefix + separator : ''}${value}${suffix ? separator + suffix : ''}`;
}
const scenarioOverride = chat_metadata['scenario'];
let descriptions = [];
@ -372,10 +398,10 @@ export function getGroupCharacterCards(groupId, characterId) {
continue;
}
descriptions.push(baseChatReplace(character.description.trim(), name1, character.name));
personalities.push(baseChatReplace(character.personality.trim(), name1, character.name));
scenarios.push(baseChatReplace(character.scenario.trim(), name1, character.name));
mesExamplesArray.push(baseChatReplace(character.mes_example.trim(), name1, character.name));
descriptions.push(replaceAndPrepareForJoin(character.description, character.name, 'Description'));
personalities.push(replaceAndPrepareForJoin(character.personality, character.name, 'Personality'));
scenarios.push(replaceAndPrepareForJoin(character.scenario, character.name, 'Scenario'));
mesExamplesArray.push(replaceAndPrepareForJoin(character.mes_example, character.name, 'Example Messages'));
}
const description = descriptions.filter(x => x.length).join('\n');
@ -1093,6 +1119,8 @@ async function onGroupGenerationModeInput(e) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
_thisGroup.generation_mode = Number(e.target.value);
await editGroup(openGroupId, false, false);
toggleHiddenControls(_thisGroup);
}
}
@ -1105,6 +1133,15 @@ async function onGroupAutoModeDelayInput(e) {
}
}
async function onGroupGenerationModeTemplateInput(e) {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
const prop = $(e.target).attr('setting');
_thisGroup[prop] = String(e.target.value);
await editGroup(openGroupId, false, false);
}
}
async function onGroupNameInput() {
if (openGroupId) {
let _thisGroup = groups.find((x) => x.id == openGroupId);
@ -1270,6 +1307,14 @@ async function onHideMutedSpritesClick(value) {
}
}
function toggleHiddenControls(group, generationMode = null) {
const isJoin = [group_generation_mode.APPEND, group_generation_mode.APPEND_DISABLED].includes(generationMode ?? group?.generation_mode);
$('#rm_group_generation_mode_join_prefix').parent().toggle(isJoin);
$('#rm_group_generation_mode_join_suffix').parent().toggle(isJoin);
initScrollHeight($('#rm_group_generation_mode_join_prefix'));
initScrollHeight($('#rm_group_generation_mode_join_suffix'));
}
function select_group_chats(groupId, skipAnimation) {
openGroupId = groupId;
newGroupMembers = [];
@ -1305,6 +1350,10 @@ function select_group_chats(groupId, skipAnimation) {
$('#rm_group_hidemutedsprites').prop('checked', group && group.hideMutedSprites);
$('#rm_group_automode_delay').val(group?.auto_mode_delay ?? DEFAULT_AUTO_MODE_DELAY);
$('#rm_group_generation_mode_join_prefix').val(group?.generation_mode_join_prefix ?? '').attr('setting', 'generation_mode_join_prefix');
$('#rm_group_generation_mode_join_suffix').val(group?.generation_mode_join_suffix ?? '').attr('setting', 'generation_mode_join_suffix');
toggleHiddenControls(group, generationMode);
// bottom buttons
if (openGroupId) {
$('#rm_group_submit').hide();
@ -1338,6 +1387,11 @@ function select_group_chats(groupId, skipAnimation) {
$('#rm_group_automode_label').hide();
}
// Toggle textbox sizes, as input events have not fired here
$('#rm_group_chats_block .autoSetHeight').each(element => {
resetScrollHeight(element);
});
eventSource.emit('groupSelected', { detail: { id: openGroupId, group: group } });
}
@ -1796,6 +1850,10 @@ function doCurMemberListPopout() {
}
jQuery(() => {
$(document).on('input', '#rm_group_chats_block .autoSetHeight', function () {
resetScrollHeight($(this));
});
$(document).on('click', '.group_select', function () {
const groupId = $(this).attr('chid') || $(this).attr('grid') || $(this).data('id');
openGroupById(groupId);
@ -1823,6 +1881,8 @@ jQuery(() => {
$('#rm_group_activation_strategy').on('change', onGroupActivationStrategyInput);
$('#rm_group_generation_mode').on('change', onGroupGenerationModeInput);
$('#rm_group_automode_delay').on('input', onGroupAutoModeDelayInput);
$('#rm_group_generation_mode_join_prefix').on('input', onGroupGenerationModeTemplateInput);
$('#rm_group_generation_mode_join_suffix').on('input', onGroupGenerationModeTemplateInput);
$('#group_avatar_button').on('input', uploadGroupAvatar);
$('#rm_group_restore_avatar').on('click', restoreGroupAvatar);
$(document).on('click', '.group_member .right_menu_button', onGroupActionClick);