Merge branch 'staging' of https://github.com/Cohee1207/SillyTavern into staging

This commit is contained in:
RossAscends
2023-11-04 11:16:14 +09:00
5 changed files with 184 additions and 18 deletions

73
public/scripts/chats.js Normal file
View File

@@ -0,0 +1,73 @@
// Move chat functions here from script.js (eventually)
import {
chat,
getCurrentChatId,
hideSwipeButtons,
saveChatConditional,
showSwipeButtons,
} from "../script.js";
/**
* Mark message as hidden (system message).
* @param {number} messageId Message ID
* @param {JQuery<Element>} messageBlock Message UI element
* @returns
*/
export async function hideChatMessage(messageId, messageBlock) {
const chatId = getCurrentChatId();
if (!chatId || isNaN(messageId)) return;
const message = chat[messageId];
if (!message) return;
message.is_system = true;
messageBlock.attr('is_system', String(true));
// Reload swipes. Useful when a last message is hidden.
hideSwipeButtons();
showSwipeButtons();
await saveChatConditional();
}
/**
* Mark message as visible (non-system message).
* @param {number} messageId Message ID
* @param {JQuery<Element>} messageBlock Message UI element
* @returns
*/
export async function unhideChatMessage(messageId, messageBlock) {
const chatId = getCurrentChatId();
if (!chatId || isNaN(messageId)) return;
const message = chat[messageId];
if (!message) return;
message.is_system = false;
messageBlock.attr('is_system', String(false));
// Reload swipes. Useful when a last message is hidden.
hideSwipeButtons();
showSwipeButtons();
await saveChatConditional();
}
jQuery(function() {
$(document).on('click', '.mes_hide', async function() {
const messageBlock = $(this).closest('.mes');
const messageId = Number(messageBlock.attr('mesid'));
await hideChatMessage(messageId, messageBlock);
});
$(document).on('click', '.mes_unhide', async function() {
const messageBlock = $(this).closest('.mes');
const messageId = Number(messageBlock.attr('mesid'));
await unhideChatMessage(messageId, messageBlock);
});
})

View File

@@ -119,19 +119,9 @@ const promptTemplates = {
}
const helpString = [
`${m('(argument)')} requests SD to make an image. Supported arguments:`,
'<ul>',
`<li>${m(j(triggerWords[generationMode.CHARACTER]))} AI character full body selfie</li>`,
`<li>${m(j(triggerWords[generationMode.FACE]))} AI character face-only selfie</li>`,
`<li>${m(j(triggerWords[generationMode.USER]))} user character full body selfie</li>`,
`<li>${m(j(triggerWords[generationMode.SCENARIO]))} visual recap of the whole chat scenario</li>`,
`<li>${m(j(triggerWords[generationMode.NOW]))} visual recap of the last chat message</li>`,
`<li>${m(j(triggerWords[generationMode.RAW_LAST]))} visual recap of the last chat message with no summary</li>`,
`<li>${m(j(triggerWords[generationMode.BACKGROUND]))} generate a background for this chat based on the chat's context</li>`,
'</ul>',
`Anything else would trigger a "free mode" to make SD generate whatever you prompted.<Br>
example: '/sd apple tree' would generate a picture of an apple tree.`,
].join('<br>');
`${m('(argument)')} requests SD to make an image. Supported arguments: ${m(j(Object.values(triggerWords).flat()))}.`,
`Anything else would trigger a "free mode" to make SD generate whatever you prompted. Example: '/sd apple tree' would generate a picture of an apple tree.`,
].join(' ');
const defaultPrefix = 'best quality, absurdres, aesthetic,';
const defaultNegative = 'lowres, bad anatomy, bad hands, text, error, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry';

View File

@@ -31,6 +31,7 @@ import { getRegexedString, regex_placement } from "./extensions/regex/engine.js"
import { chat_styles, power_user } from "./power-user.js";
import { autoSelectPersona } from "./personas.js";
import { getContext } from "./extensions.js";
import { hideChatMessage, unhideChatMessage } from "./chats.js";
export {
executeSlashCommands,
registerSlashCommand,
@@ -40,7 +41,7 @@ export {
class SlashCommandParser {
constructor() {
this.commands = {};
this.helpStrings = [];
this.helpStrings = {};
}
addCommand(command, callback, aliases, helpString = '', interruptsGeneration = false, purgeFromMessage = true) {
@@ -63,7 +64,7 @@ class SlashCommandParser {
let aliasesString = `(alias: ${aliases.map(x => `<span class="monospace">/${x}</span>`).join(', ')})`;
stringBuilder += aliasesString;
}
this.helpStrings.push(stringBuilder);
this.helpStrings[command] = stringBuilder;
}
parse(text) {
@@ -107,7 +108,12 @@ class SlashCommandParser {
}
getHelpString() {
const listItems = this.helpStrings.map(x => `<li>${x}</li>`).join('\n');
const listItems = Object
.entries(this.helpStrings)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(x => x[1])
.map(x => `<li>${x}</li>`)
.join('\n');
return `<p>Slash commands:</p><ol>${listItems}</ol>
<small>Slash commands can be batched into a single input by adding a pipe character | at the end, and then writing a new slash command.</small>
<ul><li><small>Example:</small><code>/cut 1 | /sys Hello, | /continue</code></li>
@@ -138,6 +144,8 @@ parser.addCommand('ask', askCharacter, [], '<span class="monospace">(prompt)</sp
parser.addCommand('delname', deleteMessagesByNameCallback, ['cancel'], '<span class="monospace">(name)</span> deletes all messages attributed to a specified name', true, true);
parser.addCommand('send', sendUserMessageCallback, ['add'], '<span class="monospace">(text)</span> adds a user message to the chat log without triggering a generation', true, true);
parser.addCommand('trigger', triggerGroupMessageCallback, [], '<span class="monospace">(member index or name)</span> triggers a message generation for the specified group member', true, true);
parser.addCommand('hide', hideMessageCallback, [], '<span class="monospace">(message index)</span> hides a chat message from the prompt', true, true);
parser.addCommand('unhide', unhideMessageCallback, [], '<span class="monospace">(message index)</span> unhides a message from the prompt', true, true);
const NARRATOR_NAME_KEY = 'narrator_name';
const NARRATOR_NAME_DEFAULT = 'System';
@@ -225,6 +233,40 @@ async function askCharacter(_, text) {
eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, restoreCharacter);
}
async function hideMessageCallback(_, arg) {
if (!arg) {
console.warn('WARN: No argument provided for /hide command');
return;
}
const messageId = Number(arg);
const messageBlock = $(`.mes[mesid="${messageId}"]`);
if (!messageBlock.length) {
console.warn(`WARN: No message found with ID ${messageId}`);
return;
}
await hideChatMessage(messageId, messageBlock);
}
async function unhideMessageCallback(_, arg) {
if (!arg) {
console.warn('WARN: No argument provided for /unhide command');
return;
}
const messageId = Number(arg);
const messageBlock = $(`.mes[mesid="${messageId}"]`);
if (!messageBlock.length) {
console.warn(`WARN: No message found with ID ${messageId}`);
return;
}
await unhideChatMessage(messageId, messageBlock);
}
async function triggerGroupMessageCallback(_, arg) {
if (!selected_group) {
toastr.warning("Cannot run this command outside of a group chat.");
@@ -680,3 +722,42 @@ function executeSlashCommands(text) {
return { interrupt, newText };
}
function setSlashCommandAutocomplete(textarea) {
textarea.autocomplete({
source: (input, output) => {
// Only show for slash commands and if there's no space
if (!input.term.startsWith('/') || input.term.includes(' ')) {
output([]);
return;
}
const slashCommand = input.term.toLowerCase().substring(1); // Remove the slash
const result = Object
.keys(parser.helpStrings) // Get all slash commands
.filter(x => x.startsWith(slashCommand)) // Filter by the input
.sort((a, b) => a.localeCompare(b)) // Sort alphabetically
// .slice(0, 20) // Limit to 20 results
.map(x => ({ label: parser.helpStrings[x], value: `/${x} ` })); // Map to the help string
output(result); // Return the results
},
select: (e, u) => {
// unfocus the input
$(e.target).val(u.item.value);
},
minLength: 1,
position: { my: "left bottom", at: "left top", collision: "none" },
});
textarea.autocomplete("instance")._renderItem = function (ul, item) {
const width = $(textarea).innerWidth();
const content = $('<div></div>').html(item.label);
return $("<li>").width(width).append(content).appendTo(ul);
};
}
jQuery(function () {
const textarea = $('#send_textarea');
setSlashCommandAutocomplete(textarea);
})