Add extension setting for fallback expression

This commit is contained in:
Wolfsblvt
2024-04-07 06:12:52 +02:00
parent 17537492b2
commit 2ffb44b4e1
2 changed files with 58 additions and 13 deletions

View File

@ -11,7 +11,7 @@ const MODULE_NAME = 'expressions';
const UPDATE_INTERVAL = 2000; const UPDATE_INTERVAL = 2000;
const STREAMING_UPDATE_INTERVAL = 6000; const STREAMING_UPDATE_INTERVAL = 6000;
const TALKINGCHECK_UPDATE_INTERVAL = 500; const TALKINGCHECK_UPDATE_INTERVAL = 500;
const FALLBACK_EXPRESSION = 'joy'; const DEFAULT_FALLBACK_EXPRESSION = 'joy';
const DEFAULT_EXPRESSIONS = [ const DEFAULT_EXPRESSIONS = [
'talkinghead', 'talkinghead',
'admiration', 'admiration',
@ -58,6 +58,14 @@ function isTalkingHeadEnabled() {
return extension_settings.expressions.talkinghead && !extension_settings.expressions.local; return extension_settings.expressions.talkinghead && !extension_settings.expressions.local;
} }
/**
* Returns the fallback expression if explicitly chosen, otherwise the default one
* @returns {string} expression name
*/
function getFallbackExpression() {
return extension_settings.expressions.fallback_expression ?? DEFAULT_FALLBACK_EXPRESSION;
}
/** /**
* Toggles Talkinghead mode on/off. * Toggles Talkinghead mode on/off.
* *
@ -157,7 +165,8 @@ async function visualNovelSetCharacterSprites(container, name, expression) {
const sprites = spriteCache[spriteFolderName]; const sprites = spriteCache[spriteFolderName];
const expressionImage = container.find(`.expression-holder[data-avatar="${avatar}"]`); const expressionImage = container.find(`.expression-holder[data-avatar="${avatar}"]`);
const defaultSpritePath = sprites.find(x => x.label === FALLBACK_EXPRESSION)?.path; const defaultExpression = getFallbackExpression();
const defaultSpritePath = sprites.find(x => x.label === defaultExpression)?.path;
const noSprites = sprites.length === 0; const noSprites = sprites.length === 0;
if (expressionImage.length > 0) { if (expressionImage.length > 0) {
@ -568,7 +577,7 @@ function handleImageChange() {
// This preserves the same expression Talkinghead had at the moment it was switched off. // This preserves the same expression Talkinghead had at the moment it was switched off.
const charName = getContext().name2; const charName = getContext().name2;
const last = lastExpression[charName]; const last = lastExpression[charName];
const targetExpression = last ? last : FALLBACK_EXPRESSION; const targetExpression = last ? last : getFallbackExpression();
setExpression(charName, targetExpression, true); setExpression(charName, targetExpression, true);
} }
} }
@ -691,8 +700,8 @@ async function moduleWorker() {
const force = !!context.groupId; const force = !!context.groupId;
// Character won't be angry on you for swiping // Character won't be angry on you for swiping
if (currentLastMessage.mes == '...' && expressionsList.includes(FALLBACK_EXPRESSION)) { if (currentLastMessage.mes == '...' && expressionsList.includes(getFallbackExpression())) {
expression = FALLBACK_EXPRESSION; expression = getFallbackExpression();
} }
await sendExpressionCall(spriteFolderName, expression, force, vnMode); await sendExpressionCall(spriteFolderName, expression, force, vnMode);
@ -965,7 +974,7 @@ function sampleClassifyText(text) {
async function getExpressionLabel(text) { async function getExpressionLabel(text) {
// Return if text is undefined, saving a costly fetch request // Return if text is undefined, saving a costly fetch request
if ((!modules.includes('classify') && !extension_settings.expressions.local) || !text) { if ((!modules.includes('classify') && !extension_settings.expressions.local) || !text) {
return FALLBACK_EXPRESSION; return getFallbackExpression();
} }
text = sampleClassifyText(text); text = sampleClassifyText(text);
@ -1004,7 +1013,7 @@ async function getExpressionLabel(text) {
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
return FALLBACK_EXPRESSION; return getFallbackExpression();
} }
} }
@ -1108,6 +1117,11 @@ async function getSpritesList(name) {
} }
} }
async function renderAdditionalExpressionSettings() {
renderCustomExpressions();
await renderFallbackExpressionPicker();
}
function renderCustomExpressions() { function renderCustomExpressions() {
if (!Array.isArray(extension_settings.expressions.custom)) { if (!Array.isArray(extension_settings.expressions.custom)) {
extension_settings.expressions.custom = []; extension_settings.expressions.custom = [];
@ -1128,6 +1142,23 @@ function renderCustomExpressions() {
} }
} }
async function renderFallbackExpressionPicker() {
const expressions = await getExpressionsList();
const defaultPicker = $('#expression_fallback');
defaultPicker.empty();
const fallbackExpression = getFallbackExpression();
for (const expression of expressions) {
const option = document.createElement('option');
option.value = expression;
option.text = expression;
option.selected = expression == fallbackExpression;
defaultPicker.append(option);
}
}
async function getExpressionsList() { async function getExpressionsList() {
// Return cached list if available // Return cached list if available
if (Array.isArray(expressionsList)) { if (Array.isArray(expressionsList)) {
@ -1365,7 +1396,7 @@ async function onClickExpressionAddCustom() {
// Add custom expression into settings // Add custom expression into settings
extension_settings.expressions.custom.push(expressionName); extension_settings.expressions.custom.push(expressionName);
renderCustomExpressions(); await renderAdditionalExpressionSettings();
saveSettingsDebounced(); saveSettingsDebounced();
// Force refresh sprites list // Force refresh sprites list
@ -1392,7 +1423,7 @@ async function onClickExpressionRemoveCustom() {
// Remove custom expression from settings // Remove custom expression from settings
const index = extension_settings.expressions.custom.indexOf(selectedExpression); const index = extension_settings.expressions.custom.indexOf(selectedExpression);
extension_settings.expressions.custom.splice(index, 1); extension_settings.expressions.custom.splice(index, 1);
renderCustomExpressions(); await renderAdditionalExpressionSettings();
saveSettingsDebounced(); saveSettingsDebounced();
// Force refresh sprites list // Force refresh sprites list
@ -1401,6 +1432,14 @@ async function onClickExpressionRemoveCustom() {
moduleWorker(); moduleWorker();
} }
function onExpressionFallbackChanged() {
const expression = this.value;
if (expression) {
extension_settings.expressions.fallback_expression = expression;
saveSettingsDebounced();
}
}
async function handleFileUpload(url, formData) { async function handleFileUpload(url, formData) {
try { try {
const data = await jQuery.ajax({ const data = await jQuery.ajax({
@ -1648,7 +1687,7 @@ async function fetchImagesNoCache() {
return await Promise.allSettled(promises); return await Promise.allSettled(promises);
} }
(function () { (async function () {
function addExpressionImage() { function addExpressionImage() {
const html = ` const html = `
<div id="expression-wrapper"> <div id="expression-wrapper">
@ -1668,7 +1707,7 @@ async function fetchImagesNoCache() {
element.hide(); element.hide();
$('body').append(element); $('body').append(element);
} }
function addSettings() { async function addSettings() {
$('#extensions_settings').append(renderExtensionTemplate(MODULE_NAME, 'settings')); $('#extensions_settings').append(renderExtensionTemplate(MODULE_NAME, 'settings'));
$('#expression_override_button').on('click', onClickExpressionOverrideButton); $('#expression_override_button').on('click', onClickExpressionOverrideButton);
$('#expressions_show_default').on('input', onExpressionsShowDefaultInput); $('#expressions_show_default').on('input', onExpressionsShowDefaultInput);
@ -1696,10 +1735,11 @@ async function fetchImagesNoCache() {
} }
}); });
renderCustomExpressions(); await renderAdditionalExpressionSettings();
$('#expression_custom_add').on('click', onClickExpressionAddCustom); $('#expression_custom_add').on('click', onClickExpressionAddCustom);
$('#expression_custom_remove').on('click', onClickExpressionRemoveCustom); $('#expression_custom_remove').on('click', onClickExpressionRemoveCustom);
$('#expression_fallback').on('change', onExpressionFallbackChanged)
} }
// Pause Talkinghead to save resources when the ST tab is not visible or the window is minimized. // Pause Talkinghead to save resources when the ST tab is not visible or the window is minimized.
@ -1732,7 +1772,7 @@ async function fetchImagesNoCache() {
addExpressionImage(); addExpressionImage();
addVisualNovelMode(); addVisualNovelMode();
addSettings(); await addSettings();
const wrapper = new ModuleWorkerWrapper(moduleWorker); const wrapper = new ModuleWorkerWrapper(moduleWorker);
const updateFunction = wrapper.update.bind(wrapper); const updateFunction = wrapper.update.bind(wrapper);
setInterval(updateFunction, UPDATE_INTERVAL); setInterval(updateFunction, UPDATE_INTERVAL);

View File

@ -18,6 +18,11 @@
<input id="image_type_toggle" type="checkbox"> <input id="image_type_toggle" type="checkbox">
<span>Image Type - talkinghead (extras)</span> <span>Image Type - talkinghead (extras)</span>
</label> </label>
<div class="expression_fallback_block m-b-1 m-t-1">
<label for="expression_fallback">Default / Fallback Expression</label>
<small>Set the default and fallback expression being used when no matching expression is found.</small>
<select id="expression_fallback" class="flex1 margin0" data-i18n="Fallback Expression" placeholder="Fallback Expression"></select>
</div>
<div class="expression_custom_block m-b-1 m-t-1"> <div class="expression_custom_block m-b-1 m-t-1">
<label for="expression_custom">Custom Expressions</label> <label for="expression_custom">Custom Expressions</label>
<small>Can be set manually or with an <tt>/emote</tt> slash command.</small> <small>Can be set manually or with an <tt>/emote</tt> slash command.</small>