diff --git a/public/index.html b/public/index.html
index 11662d5a6..9456f7a25 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2394,6 +2394,7 @@
${characterName}
+
+
+
+
+
+
![]()
+
diff --git a/public/script.js b/public/script.js
index faf1d247e..063d5cc98 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1030,13 +1030,14 @@ function getMessageFromTemplate({ mesId, characterName, isUser, avatarImg, bias,
return mes;
}
-function appendImageToMessage(mes, messageElement) {
+export function appendImageToMessage(mes, messageElement) {
if (mes.extra?.image) {
- const image = document.createElement("img");
- image.src = mes.extra?.image;
- image.title = mes.extra?.title || mes.title;
- image.classList.add("img_extra");
- mes.extra?.inline_image?messageElement.find(".mes_text").append(image):messageElement.find(".mes_text").prepend(image);
+ const image = messageElement.find('.mes_img');
+ const isInline = !!mes.extra?.inline_image;
+ image.attr('src', mes.extra?.image);
+ image.attr('title', mes.extra?.title || mes.title);
+ messageElement.find(".mes_img_container").addClass("img_extra");
+ image.toggleClass("img_inline", isInline);
}
}
@@ -4479,6 +4480,40 @@ export function cancelTtsPlay() {
}
}
+async function deleteMessageImage() {
+ const value = await callPopup("Delete image from message?
This action can't be undone.
", 'confirm');
+
+ if (!value) {
+ return;
+ }
+
+ const mesBlock = $(this).closest('.mes');
+ const mesId = mesBlock.attr('mesid');
+ const message = chat[mesId];
+ delete message.extra.image;
+ delete message.extra.inline_image;
+ mesBlock.find('.mes_img_container').removeClass('img_extra');
+ mesBlock.find('.mes_img').attr('src', '');
+ saveChatConditional();
+}
+
+function enlargeMessageImage() {
+ const mesBlock = $(this).closest('.mes');
+ const mesId = mesBlock.attr('mesid');
+ const message = chat[mesId];
+ const imgSrc = message?.extra?.image;
+
+ if (!imgSrc) {
+ return;
+ }
+
+ const img = document.createElement('img');
+ img.classList.add('img_enlarged');
+ img.src = imgSrc;
+ $('#dialogue_popup').addClass('wide_dialogue_popup');
+ callPopup(img.outerHTML, 'text');
+}
+
window["SillyTavern"].getContext = function () {
return {
chat: chat,
@@ -6433,6 +6468,9 @@ $(document).ready(function () {
$('.code-copied').css({ 'display': 'none' });
});
+ $(document).on('click', '.mes_img_enlarge', enlargeMessageImage);
+ $(document).on('click', '.mes_img_delete', deleteMessageImage);
+
$(window).on('beforeunload', () => {
cancelTtsPlay();
if (streamingProcessor) {
diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js
index a4f24194c..e22b6b49c 100644
--- a/public/scripts/extensions/stable-diffusion/index.js
+++ b/public/scripts/extensions/stable-diffusion/index.js
@@ -7,7 +7,8 @@ import {
callPopup,
getRequestHeaders,
event_types,
- eventSource
+ eventSource,
+ appendImageToMessage
} from "../../../script.js";
import { getApiUrl, getContext, extension_settings, defaultRequestArgs, modules } from "../../extensions.js";
import { stringFormat, initScrollHeight, resetScrollHeight } from "../../utils.js";
@@ -477,9 +478,6 @@ async function sendMessage(prompt, image) {
}
function addSDGenButtons() {
- const messageButtonHtml = `
-
- `;
const buttonHtml = `
@@ -501,7 +499,6 @@ function addSDGenButtons() {
`;
- $('.mes_buttons').prepend(messageButtonHtml);
$('#send_but_sheld').prepend(buttonHtml);
$('#send_but_sheld').prepend(waitButtonHtml);
$(document.body).append(dropdownHtml);
@@ -519,6 +516,8 @@ function addSDGenButtons() {
placement: 'top-start',
});
+ $(document).on('click', '.sd_message_gen', sdMessageButton);
+
$(document).on('click touchend', function (e) {
const target = $(e.target);
if (target.is(dropdown)) return;
@@ -538,11 +537,11 @@ async function moduleWorker() {
if (context.onlineStatus === 'no_connection'){
$('#sd_gen').hide(200);
- $('.sd_message_gen').hide(200);
+ $('.sd_message_gen').hide();
}
else{
$('#sd_gen').show(200);
- $('.sd_message_gen').show(200);
+ $('.sd_message_gen').show();
}
}
@@ -551,31 +550,32 @@ addSDGenButtons();
setInterval(moduleWorker, UPDATE_INTERVAL);
function sdMessageButton (e) {
- const character = $(e.currentTarget).parents('div.mes_block').children('div.ch_name').children('span.name_text').text(),
- message = $(e.currentTarget).parents('div.mes_block').children('div.mes_text').text();
+ const $mes = $(e.currentTarget).closest('.mes');
+ const character = $mes.find('.name_text').text(),
+ message = $mes.find('.mes_text').text();
console.log("doing /sd raw last");
- generatePicture('sd', 'raw_last', `${character} said: ${message}`, appendImageToMessage);
+ generatePicture('sd', 'raw_last', `${character} said: ${message}`, saveGeneratedImage);
- function appendImageToMessage(prompt, image){
- const sd_image = document.createElement("img"),
- context = getContext(),
- message_id = $(e.target).parents('div.mes').attr('mesid');
- sd_image.src = image;
- sd_image.title = prompt;
- sd_image.classList.add("img_extra");
- $(e.target).parents('div.mes_block').children('div.mes_text').append(sd_image);
+ function saveGeneratedImage(prompt, image){
+ const context = getContext();
+ const message_id = $mes.attr('mesid');
+ const message = context.chat[message_id];
- context.chat[message_id].extra.inline_image = true;
- context.chat[message_id].extra.image = image;
- context.chat[message_id].extra.title = prompt;
+ // Some message sources may not create the extra object
+ if (typeof message.extra !== 'object') {
+ message.extra = {};
+ }
+
+ message.extra.inline_image = true;
+ message.extra.image = image;
+ message.extra.title = prompt;
+ appendImageToMessage(message, $mes);
context.saveChat();
}
};
-window.sdMessageButton = sdMessageButton;
-
$("#sd_dropdown [id]").on("click", function () {
var id = $(this).attr("id");
if (id == "sd_you") {
@@ -672,4 +672,5 @@ jQuery(async () => {
});
await loadSettings();
+ $('body').addClass('sd');
});
diff --git a/public/style.css b/public/style.css
index b6480a705..6f953e9ec 100644
--- a/public/style.css
+++ b/public/style.css
@@ -204,12 +204,14 @@ table.responsiveTable {
text-align: center;
}
+.sd_message_gen,
.mes_narrate,
body.tts .mes[is_user="true"] .mes_narrate,
body.tts .mes[is_system="true"] .mes_narrate {
display: none;
}
+body.sd .sd_message_gen,
body.tts .mes_narrate {
display: inline-block;
}
@@ -2187,6 +2189,7 @@ input[type="range"]::-webkit-slider-thumb {
.mes_prompt,
.mes_narrate,
+.sd_message_gen,
.mes_copy,
.mes_edit {
cursor: pointer;
@@ -2199,11 +2202,13 @@ input[type="range"]::-webkit-slider-thumb {
.mes_edit:hover,
.mes_copy:hover,
+.sd_message_gen:hover,
.mes_narrate:hover,
.mes_stop:hover {
opacity: 1;
}
+.last_mes .sd_message_gen,
.last_mes .mes_copy,
.last_mes .mes_narrate,
.last_mes .mes_prompt {
@@ -3307,16 +3312,59 @@ a {
}
/* Message images */
-.mes img.img_extra {
+.mes .mes_img_container {
max-width: 100%;
max-height: 60svh;
/*to fit inside single window height of mobile landscape*/
- border-radius: 10px;
- display: block;
+ display: none;
+ position: relative;
+ width: fit-content;
+ transition: all 0.1s;
}
-.mes img.img_extra~* {
+.mes_img {
+ border-radius: 10px;
+ width: 100%;
+}
+
+.mes_img_controls {
+ position: absolute;
+ top: 0.5em;
+ left: 0;
+ width: 100%;
display: none;
+ flex-direction: row;
+ justify-content: space-between;
+ padding: 0.75em;
+}
+
+.mes_img_controls .right_menu_button {
+ padding: 0;
+ filter: brightness(80%);
+}
+
+.mes_img_controls .right_menu_button:hover {
+ filter: brightness(150%);
+}
+
+/*
+.mes_img_container:hover .mes_img {
+ opacity: 0.9;
+}
+*/
+
+.mes_img_container:hover .mes_img_controls {
+ display: flex;
+}
+
+.mes .mes_img_container.img_extra {
+ display: flex;
+}
+
+.img_enlarged {
+ width: 100%;
+ padding: 10px 0;
+ border-radius: 2px;
}
/* Extensions */