Slash command to set sprite / emote. Also allow to do it per click even in online mode

This commit is contained in:
Cohee 2023-09-07 17:48:12 +03:00
parent f2cae64b0d
commit bbe52886da
1 changed files with 68 additions and 76 deletions

View File

@ -2,6 +2,7 @@ import { callPopup, eventSource, event_types, getRequestHeaders, saveSettingsDeb
import { dragElement, isMobile } from "../../RossAscends-mods.js"; import { dragElement, isMobile } from "../../RossAscends-mods.js";
import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js"; import { getContext, getApiUrl, modules, extension_settings, ModuleWorkerWrapper, doExtrasFetch, renderExtensionTemplate } from "../../extensions.js";
import { loadMovingUIState, power_user } from "../../power-user.js"; import { loadMovingUIState, power_user } from "../../power-user.js";
import { registerSlashCommand } from "../../slash-commands.js";
import { onlyUnique, debounce, getCharaFilename } from "../../utils.js"; import { onlyUnique, debounce, getCharaFilename } from "../../utils.js";
export { MODULE_NAME }; export { MODULE_NAME };
@ -125,15 +126,7 @@ async function visualNovelSetCharacterSprites(container, name, expression) {
continue; continue;
} }
let spriteFolderName = character.name; const spriteFolderName = getSpriteFolderName({ original_avatar: character.avatar }, character.name);
const avatarFileName = getSpriteFolderName({ original_avatar: character.avatar });
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
e.name == avatarFileName
);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
// download images if not downloaded yet // download images if not downloaded yet
if (spriteCache[spriteFolderName] === undefined) { if (spriteCache[spriteFolderName] === undefined) {
@ -270,16 +263,7 @@ async function setLastMessageSprite(img, avatar, labels) {
if (lastMessage) { if (lastMessage) {
const text = lastMessage.mes || ''; const text = lastMessage.mes || '';
let spriteFolderName = lastMessage.name; const spriteFolderName = getSpriteFolderName(lastMessage);
const avatarFileName = getSpriteFolderName(lastMessage);
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
e.name == avatarFileName
);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
const sprites = spriteCache[spriteFolderName] || []; const sprites = spriteCache[spriteFolderName] || [];
const label = await getExpressionLabel(text); const label = await getExpressionLabel(text);
const path = labels.includes(label) ? sprites.find(x => x.label === label)?.path : ''; const path = labels.includes(label) ? sprites.find(x => x.label === label)?.path : '';
@ -365,7 +349,7 @@ async function setImage(img, path) {
expressionClone.removeClass('default'); expressionClone.removeClass('default');
expressionClone.off('error'); expressionClone.off('error');
expressionClone.on('error', function () { expressionClone.on('error', function () {
console.debug('Expression image error', sprite.path); console.debug('Expression image error', path);
$(this).attr('src', ''); $(this).attr('src', '');
$(this).off('error'); $(this).off('error');
resolve(); resolve();
@ -419,17 +403,7 @@ async function loadLiveChar() {
return; return;
} }
const context = getContext(); const spriteFolderName = getSpriteFolderName();
let spriteFolderName = context.name2;
const message = getLastCharacterMessage();
const avatarFileName = getSpriteFolderName(message);
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
e.name == avatarFileName
);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`; const talkingheadPath = `/characters/${encodeURIComponent(spriteFolderName)}/talkinghead.png`;
@ -468,7 +442,7 @@ async function loadLiveChar() {
function handleImageChange() { function handleImageChange() {
const imgElement = document.querySelector('img#expression-image.expression'); const imgElement = document.querySelector('img#expression-image.expression');
if (!imgElement) { if (!imgElement || !(imgElement instanceof HTMLImageElement)) {
console.log("Cannot find addExpressionImage()"); console.log("Cannot find addExpressionImage()");
return; return;
} }
@ -480,7 +454,7 @@ function handleImageChange() {
if (imgElement.src !== talkingheadResultFeedSrc) { if (imgElement.src !== talkingheadResultFeedSrc) {
const expressionImageElement = document.querySelector('.expression_list_image'); const expressionImageElement = document.querySelector('.expression_list_image');
if (expressionImageElement) { if (expressionImageElement && expressionImageElement instanceof HTMLImageElement) {
doExtrasFetch(expressionImageElement.src, { doExtrasFetch(expressionImageElement.src, {
method: 'HEAD', method: 'HEAD',
}) })
@ -516,12 +490,14 @@ async function moduleWorker() {
//clear expression //clear expression
let imgElement = document.getElementById('expression-image'); let imgElement = document.getElementById('expression-image');
imgElement.src = ""; if (imgElement && imgElement instanceof HTMLImageElement) {
imgElement.src = "";
}
//set checkbox to global var //set checkbox to global var
$('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead); $('#image_type_toggle').prop('checked', extension_settings.expressions.talkinghead);
if (extension_settings.expressions.talkinghead) { if (extension_settings.expressions.talkinghead) {
settalkingheadState(extension_settings.expressions.talkinghead); setTalkingHeadState(extension_settings.expressions.talkinghead);
} }
} }
@ -545,15 +521,7 @@ async function moduleWorker() {
} }
const currentLastMessage = getLastCharacterMessage(); const currentLastMessage = getLastCharacterMessage();
let spriteFolderName = currentLastMessage.name; let spriteFolderName = getSpriteFolderName(currentLastMessage, currentLastMessage.name);
const avatarFileName = getSpriteFolderName(currentLastMessage);
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
e.name == avatarFileName
);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
// character has no expressions or it is not loaded // character has no expressions or it is not loaded
if (Object.keys(spriteCache).length === 0) { if (Object.keys(spriteCache).length === 0) {
@ -627,18 +595,8 @@ async function moduleWorker() {
} }
} }
async function talkingheadcheck() { async function talkingHeadCheck() {
const context = getContext(); let spriteFolderName = getSpriteFolderName();
let spriteFolderName = context.name2;
const message = getLastCharacterMessage();
const avatarFileName = getSpriteFolderName(message);
const expressionOverride = extension_settings.expressionOverrides.find((e) =>
e.name == avatarFileName
);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
try { try {
await validateImages(spriteFolderName); await validateImages(spriteFolderName);
@ -659,11 +617,25 @@ async function talkingheadcheck() {
} }
} }
function settalkingheadState(switch_var) { function getSpriteFolderName(characterMessage = null, characterName = null) {
const context = getContext();
let spriteFolderName = characterName ?? context.name2;
const message = characterMessage ?? getLastCharacterMessage();
const avatarFileName = getFolderNameByMessage(message);
const expressionOverride = extension_settings.expressionOverrides.find(e => e.name == avatarFileName);
if (expressionOverride && expressionOverride.path) {
spriteFolderName = expressionOverride.path;
}
return spriteFolderName;
}
function setTalkingHeadState(switch_var) {
extension_settings.expressions.talkinghead = switch_var; // Store setting extension_settings.expressions.talkinghead = switch_var; // Store setting
saveSettingsDebounced(); saveSettingsDebounced();
talkingheadcheck().then(result => { talkingHeadCheck().then(result => {
if (result) { if (result) {
//console.log("talkinghead exists!"); //console.log("talkinghead exists!");
@ -672,7 +644,7 @@ function settalkingheadState(switch_var) {
} else { } else {
unloadLiveChar(); unloadLiveChar();
} }
handleImageChange(switch_var); // Change image as needed handleImageChange(); // Change image as needed
} else { } else {
@ -681,7 +653,7 @@ function settalkingheadState(switch_var) {
}); });
} }
function getSpriteFolderName(message) { function getFolderNameByMessage(message) {
const context = getContext(); const context = getContext();
let avatarPath = ''; let avatarPath = '';
@ -712,6 +684,31 @@ async function sendExpressionCall(name, expression, force, vnMode) {
} }
} }
async function setSpriteSlashCommand(_, spriteId) {
if (!spriteId) {
console.log('No sprite id provided');
return;
}
spriteId = spriteId.trim().toLowerCase();
const spriteFolderName = getSpriteFolderName();
await validateImages(spriteFolderName);
// Fuzzy search for sprite
const fuse = new Fuse(spriteCache[spriteFolderName], { keys: ['label'] });
const results = fuse.search(spriteId);
const spriteItem = results[0]?.item;
if (!spriteItem) {
console.log('No sprite found for search term ' + spriteId);
return;
}
const vnMode = isVisualNovelMode();
await sendExpressionCall(spriteFolderName, spriteItem.label, true, vnMode);
}
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') || !text) { if (!modules.includes('classify') || !text) {
@ -968,12 +965,12 @@ async function setExpression(character, expression, force) {
} else { } else {
talkingheadcheck().then(result => { talkingHeadCheck().then(result => {
if (result) { if (result) {
// Find the <img> element with id="expression-image" and class="expression" // Find the <img> element with id="expression-image" and class="expression"
const imgElement = document.querySelector('img#expression-image.expression'); const imgElement = document.querySelector('img#expression-image.expression');
//console.log("searching"); //console.log("searching");
if (imgElement) { if (imgElement && imgElement instanceof HTMLImageElement) {
//console.log("setting value"); //console.log("setting value");
imgElement.src = getApiUrl() + '/api/talkinghead/result_feed'; imgElement.src = getApiUrl() + '/api/talkinghead/result_feed';
} }
@ -988,18 +985,10 @@ async function setExpression(character, expression, force) {
} }
function onClickExpressionImage() { function onClickExpressionImage() {
// online mode doesn't need force set
if (modules.includes('classify')) {
return;
}
const expression = $(this).attr('id'); const expression = $(this).attr('id');
const name = getLastCharacterMessage().name; setSpriteSlashCommand({}, expression);
if ($(this).find('.failure').length === 0) {
setExpression(name, expression, true);
}
} }
async function handleFileUpload(url, formData) { async function handleFileUpload(url, formData) {
try { try {
const data = await jQuery.ajax({ const data = await jQuery.ajax({
@ -1057,7 +1046,7 @@ async function onClickExpressionUpload(event) {
async function onClickExpressionOverrideButton() { async function onClickExpressionOverrideButton() {
const context = getContext(); const context = getContext();
const currentLastMessage = getLastCharacterMessage(); const currentLastMessage = getLastCharacterMessage();
const avatarFileName = getSpriteFolderName(currentLastMessage); const avatarFileName = getFolderNameByMessage(currentLastMessage);
// If the avatar name couldn't be found, abort. // If the avatar name couldn't be found, abort.
if (!avatarFileName) { if (!avatarFileName) {
@ -1066,7 +1055,7 @@ async function onClickExpressionOverrideButton() {
return; return;
} }
const overridePath = $("#expression_override").val(); const overridePath = String($("#expression_override").val());
const existingOverrideIndex = extension_settings.expressionOverrides.findIndex((e) => const existingOverrideIndex = extension_settings.expressionOverrides.findIndex((e) =>
e.name == avatarFileName e.name == avatarFileName
); );
@ -1191,7 +1180,7 @@ async function onClickExpressionDelete(event) {
function setExpressionOverrideHtml(forceClear = false) { function setExpressionOverrideHtml(forceClear = false) {
const currentLastMessage = getLastCharacterMessage(); const currentLastMessage = getLastCharacterMessage();
const avatarFileName = getSpriteFolderName(currentLastMessage); const avatarFileName = getFolderNameByMessage(currentLastMessage);
if (!avatarFileName) { if (!avatarFileName) {
return; return;
} }
@ -1249,7 +1238,9 @@ function setExpressionOverrideHtml(forceClear = false) {
$('.expression_settings').hide(); $('.expression_settings').hide();
$('#image_type_toggle').on('click', function () { $('#image_type_toggle').on('click', function () {
settalkingheadState(this.checked); if (this instanceof HTMLInputElement) {
setTalkingHeadState(this.checked);
}
}); });
} }
@ -1270,4 +1261,5 @@ function setExpressionOverrideHtml(forceClear = false) {
}); });
eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced); eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced);
eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced); eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced);
registerSlashCommand('sprite', setSpriteSlashCommand, ['emote'], '<span class="monospace">spriteId</span> force sets the sprite for the current character', true, true);
})(); })();