Update backend returned sprites list
| @@ -18,10 +18,10 @@ import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js'; | |||
| 18 | export { MODULE_NAME }; | 18 | export { MODULE_NAME }; |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | * @typedef {object} Expression Expression definition with label and file path | 21 | * @typedef {object} Expression Expression definition with label and file path |
| 22 | * @property {string} label The label of the expression | 22 | * @property {string} label The label of the expression |
| 23 | * @property {string} path The path to the expression image | 23 | * @property {ExpressionImage[]} files One or more images to represent this expression |
| 24 | */ | 24 | */ |
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 27 | * @typedef {object} ExpressionImage An expression image | 27 | * @typedef {object} ExpressionImage An expression image |
| @@ -86,6 +86,7 @@ let lastCharacter = undefined; | |||
| 86 | let lastMessage = null; | 86 | let lastMessage = null; |
| 87 | let lastTalkingState = false; | 87 | let lastTalkingState = false; |
| 88 | let lastTalkingStateMessage = null; // last message as seen by `updateTalkingState` (tracked separately, different timer) | 88 | let lastTalkingStateMessage = null; // last message as seen by `updateTalkingState` (tracked separately, different timer) |
| 89 | /** @type {{[characterKey: string]: Expression[]}} */ | ||
| 89 | let spriteCache = {}; | 90 | let spriteCache = {}; |
| 90 | let inApiCall = false; | 91 | let inApiCall = false; |
| 91 | let lastServerResponseTime = 0; | 92 | let lastServerResponseTime = 0; |
| @@ -1307,6 +1308,11 @@ function removeExpression() { | |||
| 1307 | $('#no_chat_expressions').show(); | 1308 | $('#no_chat_expressions').show(); |
| 1308 | } | 1309 | } |
| 1309 | 1310 | ||
| 1311 | /** | ||
| 1312 | * Validate a character's sprites, and redraw the sprites list if not done before or forced to redraw. | ||
| 1313 | * @param {string} character - The character to validate | ||
| 1314 | * @param {boolean} forceRedrawCached - Whether to force redrawing the sprites list even if it's already been drawn before | ||
| 1315 | */ | ||
| 1310 | async function validateImages(character, forceRedrawCached) { | 1316 | async function validateImages(character, forceRedrawCached) { |
| 1311 | if (!character) { | 1317 | if (!character) { |
| 1312 | return; | 1318 | return; |
| @@ -1330,7 +1336,7 @@ async function validateImages(character, forceRedrawCached) { | |||
| 1330 | 1336 | ||
| 1331 | /** | 1337 | /** |
| 1332 | * Takes a given sprite as returned from the server, and enriches it with additional data for display/sorting | 1338 | * Takes a given sprite as returned from the server, and enriches it with additional data for display/sorting |
| 1333 | * @param {Expression} sprite | 1339 | * @param {{ path: string, label: string }} sprite |
| 1334 | * @returns {ExpressionImage} | 1340 | * @returns {ExpressionImage} |
| 1335 | */ | 1341 | */ |
| 1336 | function getExpressionImageData(sprite) { | 1342 | function getExpressionImageData(sprite) { |
| @@ -1371,7 +1377,8 @@ async function drawSpritesList(character, labels, sprites) { | |||
| 1371 | const isCustom = extension_settings.expressions.custom?.includes(expression); | 1377 | const isCustom = extension_settings.expressions.custom?.includes(expression); |
| 1372 | const images = sprites | 1378 | const images = sprites |
| 1373 | .filter(s => s.label === expression) | 1379 | .filter(s => s.label === expression) |
| 1374 | .map(getExpressionImageData) | 1380 | .map(s => s.files) |
| 1381 | .flat() | ||
| 1375 | .sort((a, b) => a.title.localeCompare(b.title)); | 1382 | .sort((a, b) => a.title.localeCompare(b.title)); |
| 1376 | 1383 | ||
| 1377 | if (images.length === 0) { | 1384 | if (images.length === 0) { |
| @@ -1384,7 +1391,7 @@ async function drawSpritesList(character, labels, sprites) { | |||
| 1384 | } | 1391 | } |
| 1385 | 1392 | ||
| 1386 | // TODO: Fix valid expression lists/caching and group them correctly | 1393 | // TODO: Fix valid expression lists/caching and group them correctly |
| 1387 | validExpressions.push({ label: expression, paths: images }); | 1394 | validExpressions.push({ label: expression, files: images }); |
| 1388 | 1395 | ||
| 1389 | // Render main = first file, additional = rest | 1396 | // Render main = first file, additional = rest |
| 1390 | let listItem = await getListItem(expression, { | 1397 | let listItem = await getListItem(expression, { |
| @@ -1408,13 +1415,35 @@ async function getListItem(expression, { images, isCustom = false } = {}) { | |||
| 1408 | return renderExtensionTemplateAsync(MODULE_NAME, 'list-item', { expression, images, isCustom: isCustom ?? false }); | 1415 | return renderExtensionTemplateAsync(MODULE_NAME, 'list-item', { expression, images, isCustom: isCustom ?? false }); |
| 1409 | } | 1416 | } |
| 1410 | 1417 | ||
| 1418 | /** | ||
| 1419 | * Fetches and processes the list of sprites for a given character name. | ||
| 1420 | * Retrieves sprite data from the server and organizes it into labeled groups. | ||
| 1421 | * | ||
| 1422 | * @param {string} name - The character name to fetch sprites for | ||
| 1423 | * @returns {Promise<Expression[]>} A promise that resolves to an array of grouped expression objects, each containing a label and associated image data | ||
| 1424 | */ | ||
| 1425 | |||
| 1411 | async function getSpritesList(name) { | 1426 | async function getSpritesList(name) { |
| 1412 | console.debug('getting sprites list'); | 1427 | console.debug('getting sprites list'); |
| 1413 | 1428 | ||
| 1414 | try { | 1429 | try { |
| 1415 | const result = await fetch(`/api/sprites/get?name=${encodeURIComponent(name)}`); | 1430 | const result = await fetch(`/api/sprites/get?name=${encodeURIComponent(name)}`); |
| 1431 | /** @type {{ label: string, path: string }[]} */ | ||
| 1416 | let sprites = result.ok ? (await result.json()) : []; | 1432 | let sprites = result.ok ? (await result.json()) : []; |
| 1417 | return sprites; | 1433 | |
| 1434 | /** @type {Expression[]} */ | ||
| 1435 | const grouped = sprites.reduce((acc, sprite) => { | ||
| 1436 | const imageData = getExpressionImageData(sprite); | ||
| 1437 | let existingExpression = acc.find(exp => exp.label === sprite.label); | ||
| 1438 | if (existingExpression) { | ||
| 1439 | existingExpression.files.push(imageData); | ||
| 1440 | } else { | ||
| 1441 | acc.push({ label: sprite.label, files: [imageData] }); | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | return acc; | ||
| 1445 | }, []); | ||
| 1446 | return grouped; | ||
| 1418 | } | 1447 | } |
| 1419 | catch (err) { | 1448 | catch (err) { |
| 1420 | console.log(err); | 1449 | console.log(err); |