From eab25c73e7a16b667f63adb54d67ee52d0071885 Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Fri, 3 Nov 2023 23:45:56 +0200
Subject: [PATCH 1/2] Hide/unhide messages
---
public/index.html | 4 ++
public/scripts/chats.js | 73 ++++++++++++++++++++++++++++++++
public/scripts/slash-commands.js | 37 ++++++++++++++++
public/style.css | 20 ++++++++-
4 files changed, 133 insertions(+), 1 deletion(-)
create mode 100644 public/scripts/chats.js
diff --git a/public/index.html b/public/index.html
index 9e452164c..a4f5d8a11 100644
--- a/public/index.html
+++ b/public/index.html
@@ -90,6 +90,7 @@
+
SillyTavern
@@ -4348,6 +4349,7 @@
@@ -4359,6 +4361,8 @@
+
+
diff --git a/public/scripts/chats.js b/public/scripts/chats.js
new file mode 100644
index 000000000..d716a968f
--- /dev/null
+++ b/public/scripts/chats.js
@@ -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} 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} 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);
+ });
+})
diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index d7d485fa4..1d23e84ad 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -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,
@@ -138,6 +139,8 @@ parser.addCommand('ask', askCharacter, [], '(prompt)(name) – deletes all messages attributed to a specified name', true, true);
parser.addCommand('send', sendUserMessageCallback, ['add'], '(text) – adds a user message to the chat log without triggering a generation', true, true);
parser.addCommand('trigger', triggerGroupMessageCallback, [], '(member index or name) – triggers a message generation for the specified group member', true, true);
+parser.addCommand('hide', hideMessageCallback, [], '(message index) – hides a chat message from the prompt', true, true);
+parser.addCommand('unhide', unhideMessageCallback, [], '(message index) – unhides a message from the prompt', true, true);
const NARRATOR_NAME_KEY = 'narrator_name';
const NARRATOR_NAME_DEFAULT = 'System';
@@ -225,6 +228,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.");
diff --git a/public/style.css b/public/style.css
index e100c9568..2d6e1b9dd 100644
--- a/public/style.css
+++ b/public/style.css
@@ -218,6 +218,11 @@ table.responsiveTable {
display: none;
}
+.mes[is_system="true"] .avatar {
+ opacity: 0.9;
+ filter: grayscale(25%);
+}
+
.mes_text table {
border-spacing: 0;
border-collapse: collapse;
@@ -304,10 +309,23 @@ table.responsiveTable {
.mes_translate,
.sd_message_gen,
+.mes_ghost,
.mes_narrate {
display: none;
}
+.mes[is_system="true"] .mes_hide {
+ display: none;
+}
+
+.mes[is_system="false"] .mes_unhide {
+ display: none;
+}
+
+.mes[is_system="true"] .mes_ghost {
+ display: flex;
+}
+
small {
color: var(--grey70);
}
@@ -3645,4 +3663,4 @@ a {
height: 100vh;
z-index: 9999;
}
-}
\ No newline at end of file
+}
From 6067b2f9138107a03e14e4650fafb02f722858cc Mon Sep 17 00:00:00 2001
From: Cohee <18619528+Cohee1207@users.noreply.github.com>
Date: Sat, 4 Nov 2023 01:21:20 +0200
Subject: [PATCH 2/2] Add autocomplete for slash commands
---
.../extensions/stable-diffusion/index.js | 16 ++----
public/scripts/slash-commands.js | 50 +++++++++++++++++--
public/style.css | 2 +-
3 files changed, 51 insertions(+), 17 deletions(-)
diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js
index 63aea9d3e..2e5f07c73 100644
--- a/public/scripts/extensions/stable-diffusion/index.js
+++ b/public/scripts/extensions/stable-diffusion/index.js
@@ -119,19 +119,9 @@ const promptTemplates = {
}
const helpString = [
- `${m('(argument)')} – requests SD to make an image. Supported arguments:`,
- '',
- `- ${m(j(triggerWords[generationMode.CHARACTER]))} – AI character full body selfie
`,
- `- ${m(j(triggerWords[generationMode.FACE]))} – AI character face-only selfie
`,
- `- ${m(j(triggerWords[generationMode.USER]))} – user character full body selfie
`,
- `- ${m(j(triggerWords[generationMode.SCENARIO]))} – visual recap of the whole chat scenario
`,
- `- ${m(j(triggerWords[generationMode.NOW]))} – visual recap of the last chat message
`,
- `- ${m(j(triggerWords[generationMode.RAW_LAST]))} – visual recap of the last chat message with no summary
`,
- `- ${m(j(triggerWords[generationMode.BACKGROUND]))} – generate a background for this chat based on the chat's context
`,
- '
',
- `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('
');
+ `${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';
diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index 1d23e84ad..c15d8198f 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -41,7 +41,7 @@ export {
class SlashCommandParser {
constructor() {
this.commands = {};
- this.helpStrings = [];
+ this.helpStrings = {};
}
addCommand(command, callback, aliases, helpString = '', interruptsGeneration = false, purgeFromMessage = true) {
@@ -64,7 +64,7 @@ class SlashCommandParser {
let aliasesString = `(alias: ${aliases.map(x => `/${x}`).join(', ')})`;
stringBuilder += aliasesString;
}
- this.helpStrings.push(stringBuilder);
+ this.helpStrings[command] = stringBuilder;
}
parse(text) {
@@ -108,7 +108,12 @@ class SlashCommandParser {
}
getHelpString() {
- const listItems = this.helpStrings.map(x => `${x}`).join('\n');
+ const listItems = Object
+ .entries(this.helpStrings)
+ .sort((a, b) => a[0].localeCompare(b[0]))
+ .map(x => x[1])
+ .map(x => `${x}`)
+ .join('\n');
return `Slash commands:
${listItems}
Slash commands can be batched into a single input by adding a pipe character | at the end, and then writing a new slash command.
- Example:
/cut 1 | /sys Hello, | /continue
@@ -717,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 = $('').html(item.label);
+ return $("- ").width(width).append(content).appendTo(ul);
+ };
+}
+
+jQuery(function () {
+ const textarea = $('#send_textarea');
+ setSlashCommandAutocomplete(textarea);
+})
diff --git a/public/style.css b/public/style.css
index 2d6e1b9dd..baef870b0 100644
--- a/public/style.css
+++ b/public/style.css
@@ -2820,7 +2820,7 @@ body .ui-widget-content li {
align-items: center;
cursor: pointer;
opacity: 0.5;
- transition: all 200ms;
+ transition: opacity 200ms;
}
body .ui-widget-content li:hover {