mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Upload or delete a sprite image.
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { saveSettingsDebounced } from "../../../script.js";
|
||||
import { callPopup, getRequestHeaders, saveSettingsDebounced } from "../../../script.js";
|
||||
import { getContext, getApiUrl, modules, extension_settings } from "../../extensions.js";
|
||||
export { MODULE_NAME };
|
||||
|
||||
@ -240,6 +240,14 @@ function drawSpritesList(character, labels, sprites) {
|
||||
function getListItem(item, imageSrc, textClass) {
|
||||
return `
|
||||
<div id="${item}" class="expression_list_item">
|
||||
<div class="expression_list_buttons">
|
||||
<div class="menu_button expression_list_upload" title="Upload image">
|
||||
<i class="fa-solid fa-upload"></i>
|
||||
</div>
|
||||
<div class="menu_button expression_list_delete" title="Delete image">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</div>
|
||||
</div>
|
||||
<span class="expression_list_title ${textClass}">${item}</span>
|
||||
<img class="expression_list_image" src="${imageSrc}" />
|
||||
</div>
|
||||
@ -340,6 +348,80 @@ function onClickExpressionImage() {
|
||||
setExpression(name, expression, true);
|
||||
}
|
||||
}
|
||||
async function onClickExpressionUpload(event) {
|
||||
// Prevents the expression from being set
|
||||
event.stopPropagation();
|
||||
|
||||
const id = $(this).closest('.expression_list_item').attr('id');
|
||||
const name = $('#image_list').data('name');
|
||||
|
||||
const handleExpressionUploadChange = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('name', name);
|
||||
formData.append('label', id);
|
||||
formData.append('avatar', file);
|
||||
|
||||
try {
|
||||
await jQuery.ajax({
|
||||
type: "POST",
|
||||
url: "/upload_sprite",
|
||||
data: formData,
|
||||
beforeSend: function () { },
|
||||
cache: false,
|
||||
contentType: false,
|
||||
processData: false,
|
||||
});
|
||||
|
||||
// Refresh sprites list
|
||||
delete spriteCache[name];
|
||||
await validateImages(name);
|
||||
} catch (error) {
|
||||
toastr.error('Failed to upload image');
|
||||
}
|
||||
|
||||
// Reset the input
|
||||
e.target.form.reset();
|
||||
};
|
||||
|
||||
$('#expression_upload')
|
||||
.off('change')
|
||||
.on('change', handleExpressionUploadChange)
|
||||
.trigger('click');
|
||||
}
|
||||
|
||||
async function onClickExpressionDelete(event) {
|
||||
// Prevents the expression from being set
|
||||
event.stopPropagation();
|
||||
|
||||
const confirmation = await callPopup("<h3>Are you sure?</h3>Once deleted, it's gone forever!", 'confirm');
|
||||
|
||||
if (!confirmation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = $(this).closest('.expression_list_item').attr('id');
|
||||
const name = $('#image_list').data('name');
|
||||
|
||||
try {
|
||||
await fetch('/delete_sprite', {
|
||||
method: 'POST',
|
||||
headers: getRequestHeaders(),
|
||||
body: JSON.stringify({ name, label: id }),
|
||||
});
|
||||
} catch (error) {
|
||||
toastr.error('Failed to delete image. Try again later.');
|
||||
}
|
||||
|
||||
// Refresh sprites list
|
||||
delete spriteCache[name];
|
||||
await validateImages(name);
|
||||
}
|
||||
|
||||
(function () {
|
||||
function addExpressionImage() {
|
||||
@ -369,12 +451,15 @@ function onClickExpressionImage() {
|
||||
<label for="expressions_show_default"><input id="expressions_show_default" type="checkbox">Show default images (emojis) if missing</label>
|
||||
</div>
|
||||
</div>
|
||||
<form><input type="file" id="expression_upload" name="expression_upload" accept="image/*" hidden></form>
|
||||
</div>
|
||||
`;
|
||||
$('#extensions_settings').append(html);
|
||||
$('#expressions_show_default').on('input', onExpressionsShowDefaultInput);
|
||||
$('#expressions_show_default').prop('checked', extension_settings.expressions.showDefault).trigger('input');
|
||||
$(document).on('click', '.expression_list_item', onClickExpressionImage);
|
||||
$(document).on('click', '.expression_list_upload', onClickExpressionUpload);
|
||||
$(document).on('click', '.expression_list_delete', onClickExpressionDelete);
|
||||
$('.expression_settings').hide();
|
||||
}
|
||||
|
||||
|
@ -78,9 +78,23 @@ img.expression.default {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.expression_list_buttons {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 20%;
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
.expression_list_image {
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#image_list {
|
||||
|
77
server.js
77
server.js
@ -3061,6 +3061,83 @@ app.post('/google_translate', jsonParser, async (request, response) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/delete_sprite', jsonParser, async (request, response) => {
|
||||
const label = request.body.label;
|
||||
const name = request.body.name;
|
||||
|
||||
if (!label || !name) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
try {
|
||||
const spritesPath = path.join(directories.characters, name);
|
||||
|
||||
// No sprites folder exists, or not a directory
|
||||
if (!fs.existsSync(spritesPath) || !fs.statSync(spritesPath).isDirectory()) {
|
||||
return response.sendStatus(404);
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(spritesPath);
|
||||
|
||||
// Remove existing sprite with the same label
|
||||
for (const file of files) {
|
||||
if (path.parse(file).name === label) {
|
||||
fs.rmSync(path.join(spritesPath, file));
|
||||
}
|
||||
}
|
||||
|
||||
return response.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/upload_sprite', urlencodedParser, async (request, response) => {
|
||||
const file = request.file;
|
||||
const label = request.body.label;
|
||||
const name = request.body.name;
|
||||
|
||||
if (!file || !label || !name) {
|
||||
return response.sendStatus(400);
|
||||
}
|
||||
|
||||
try {
|
||||
const spritesPath = path.join(directories.characters, name);
|
||||
|
||||
// Path to sprites is not a directory. This should never happen.
|
||||
if (!fs.statSync(spritesPath).isDirectory()) {
|
||||
return response.sendStatus(404);
|
||||
}
|
||||
|
||||
// Create sprites folder if it doesn't exist
|
||||
if (!fs.existsSync(spritesPath)) {
|
||||
fs.mkdirSync(spritesPath);
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(spritesPath);
|
||||
|
||||
// Remove existing sprite with the same label
|
||||
for (const file of files) {
|
||||
if (path.parse(file).name === label) {
|
||||
fs.rmSync(path.join(spritesPath, file));
|
||||
}
|
||||
}
|
||||
|
||||
const filename = label + path.parse(file.originalname).ext;
|
||||
const spritePath = path.join("./uploads/", file.filename);
|
||||
const pathToFile = path.join(spritesPath, filename);
|
||||
// Copy uploaded file to sprites folder
|
||||
fs.cpSync(spritePath, pathToFile);
|
||||
// Remove uploaded file
|
||||
fs.rmSync(spritePath);
|
||||
return response.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return response.sendStatus(500);
|
||||
}
|
||||
});
|
||||
|
||||
function writeSecret(key, value) {
|
||||
if (!fs.existsSync(SECRETS_FILE)) {
|
||||
const emptyFile = JSON.stringify({});
|
||||
|
Reference in New Issue
Block a user