mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into parser-v2
This commit is contained in:
@ -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;
|
||||
|
@ -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. This means that in the story string for example all character descriptions will be joined to one big text. If you want those fields to be separated, you can define a prefix or suffix here. This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> 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. This means that in the story string for example all character descriptions will be joined to one big text. If you want those fields to be separated, you can define a prefix or suffix here. This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> 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="—" 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. This means that in the story string for example all character descriptions will be joined to one big text. If you want those fields to be separated, you can define a prefix or suffix here. This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> 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. This means that in the story string for example all character descriptions will be joined to one big text. If you want those fields to be separated, you can define a prefix or suffix here. This value supports normal macros and will also replace {{char}} with the relevant char's name and <FIELDNAME> 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="—" 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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user