diff --git a/.github/readme-zh_cn.md b/.github/readme-zh_cn.md index e3ff35cc7..ad0079ea4 100644 --- a/.github/readme-zh_cn.md +++ b/.github/readme-zh_cn.md @@ -291,7 +291,6 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。 * RossAscends' additions: AGPL v3 * Portions of CncAnon's TavernAITurbo mod: Unknown license * kingbri's various commits and suggestions (https://github.com/bdashore3) -* BlipRanger's miscellaneous UI & extension modifications (https://github.com/BlipRanger) * Waifu mode inspired by the work of PepperTaco (https://github.com/peppertaco/Tavern/) * Thanks Pygmalion University for being awesome testers and suggesting cool features! * Thanks oobabooga for compiling presets for TextGen @@ -303,4 +302,4 @@ SillyTavern 会将 API 密钥保存在目录中的 `secrets.json` 文件内。 * Thanks paniphons for providing a FAQ document * 10K Discord Users Celebratory Background by @kallmeflocc * Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc -* Korean translation by @doloroushyeonse \ No newline at end of file +* Korean translation by @doloroushyeonse diff --git a/.github/readme.md b/.github/readme.md index 7875e22a1..d24787189 100644 --- a/.github/readme.md +++ b/.github/readme.md @@ -293,7 +293,6 @@ GNU Affero General Public License for more details.** * RossAscends' additions: AGPL v3 * Portions of CncAnon's TavernAITurbo mod: Unknown license * kingbri's various commits and suggestions () -* BlipRanger's miscellaneous UI & extension modifications () * Waifu mode inspired by the work of PepperTaco () * Thanks Pygmalion University for being awesome testers and suggesting cool features! * Thanks oobabooga for compiling presets for TextGen @@ -306,3 +305,4 @@ GNU Affero General Public License for more details.** * 10K Discord Users Celebratory Background by @kallmeflocc * Default content (characters and lore books) provided by @OtisAlejandro, @RossAscends and @kallmeflocc * Korean translation by @doloroushyeonse +* k_euler_a support for Horde by diff --git a/.gitignore b/.gitignore index fed4e2439..be3012bd6 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ secrets.json /dist /backups/ public/movingUI/ +public/QuickReplies/ content.log diff --git a/package-lock.json b/package-lock.json index c091c2615..a803cc4a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sillytavern", - "version": "1.9.2", + "version": "1.9.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sillytavern", - "version": "1.9.2", + "version": "1.9.3", "license": "AGPL-3.0", "dependencies": { "@dqbd/tiktoken": "^1.0.2", diff --git a/package.json b/package.json index c5761758f..05dcec7bf 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "type": "git", "url": "https://github.com/SillyTavern/SillyTavern.git" }, - "version": "1.9.2", + "version": "1.9.3", "scripts": { "start": "node server.js", "pkg": "pkg --compress Gzip --no-bytecode --public ." diff --git a/public/QuickReplies/Default.json b/public/QuickReplies/Default.json new file mode 100644 index 000000000..522b479f3 --- /dev/null +++ b/public/QuickReplies/Default.json @@ -0,0 +1,23 @@ +{ + "name": "Default", + "quickReplyEnabled": true, + "quickReplySlots": [ + { + "mes": "/?", + "label": "HELP", + "enabled": true + }, + { + "mes": "/newchat", + "label": "New Chat", + "enabled": true + }, + { + "mes": "/bgcol", + "label": "Match UI to Background", + "enabled": true + } + ], + "numberOfSlots": 3, + "selectedPreset": "Default" +} diff --git a/public/index.html b/public/index.html index 913a0c55f..f175bae71 100644 --- a/public/index.html +++ b/public/index.html @@ -683,6 +683,14 @@ +
+
+ Assistant Prefill +
+
+ +
+

diff --git a/public/script.js b/public/script.js index 157d79a99..5edba339e 100644 --- a/public/script.js +++ b/public/script.js @@ -737,6 +737,9 @@ let token; var PromptArrayItemForRawPromptDisplay; +export let active_character = "" +export let active_group = "" + export function getRequestHeaders() { return { "Content-Type": "application/json", @@ -786,6 +789,14 @@ function checkOnlineStatus() { } } +export function setActiveCharacter(character) { + active_character = character; +} + +export function setActiveGroup(group) { + active_group = group; +} + async function getStatus() { if (is_get_status) { if (main_api == "koboldhorde") { @@ -5009,6 +5020,10 @@ async function getSettings(type) { highlightSelectedAvatar(); setPersonaDescription(); + //Load the active character and group + active_character = settings.active_character; + active_group = settings.active_group; + //Load the API server URL from settings api_server = settings.api_server; $("#api_url_text").val(api_server); @@ -5049,6 +5064,8 @@ async function saveSettings(type) { data: JSON.stringify({ firstRun: firstRun, username: name1, + active_character: active_character, + active_group: active_group, api_server: api_server, api_server_textgenerationwebui: api_server_textgenerationwebui, preset_settings: preset_settings, diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 7ee3d333d..52dec643d 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -13,6 +13,10 @@ import { menu_type, max_context, saveSettingsDebounced, + active_group, + active_character, + setActiveGroup, + setActiveCharacter, } from "../script.js"; import { @@ -330,11 +334,21 @@ export function RA_CountCharTokens() { characterStatsHandler(characters, this_chid); }); } -//Auto Load Last Charcter -- (fires when active_character is defined and auto_load_chat is true) +/** + * Auto load chat with the last active character or group. + * Fires when active_character is defined and auto_load_chat is true. + * The function first tries to find a character with a specific ID from the global settings. + * If it doesn't exist, it tries to find a group with a specific grid from the global settings. + * If the character list hadn't been loaded yet, it calls itself again after 100ms delay. + * The character or group is selected (clicked) if it is found. + */ async function RA_autoloadchat() { if (document.getElementById('CharID0') !== null) { - var charToAutoLoad = document.getElementById('CharID' + LoadLocal('ActiveChar')); - let groupToAutoLoad = document.querySelector(`.group_select[grid="${LoadLocal('ActiveGroup')}"]`); + // active character is the name, we should look it up in the character list and get the id + let active_character_id = Object.keys(characters).find(key => characters[key].avatar === active_character); + + var charToAutoLoad = document.getElementById('CharID' + active_character_id); + let groupToAutoLoad = document.querySelector(`.group_select[grid="${active_group}"]`); if (charToAutoLoad != null) { $(charToAutoLoad).click(); } @@ -342,7 +356,7 @@ async function RA_autoloadchat() { $(groupToAutoLoad).click(); } - // if the charcter list hadn't been loaded yet, try again. + // if the character list hadn't been loaded yet, try again. } else { setTimeout(RA_autoloadchat, 100); } } @@ -903,16 +917,22 @@ $("document").ready(function () { $("#rm_button_characters").click(function () { SaveLocal('SelectedNavTab', 'rm_button_characters'); }); // when a char is selected from the list, save them as the auto-load character for next page load + + // when a char is selected from the list, save their name as the auto-load character for next page load $(document).on("click", ".character_select", function () { - SaveLocal('ActiveChar', $(this).attr('chid')); - SaveLocal('ActiveGroup', null); + setActiveCharacter($(this).find('.avatar').attr('title')); + setActiveGroup(null); + saveSettingsDebounced(); }); $(document).on("click", ".group_select", function () { - SaveLocal('ActiveChar', null); - SaveLocal('ActiveGroup', $(this).data('id')); + setActiveCharacter(null); + setActiveGroup($(this).data('id')); + saveSettingsDebounced(); }); + + //this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height) $('#send_textarea').on('input', function () { this.style.height = '40px'; diff --git a/public/scripts/extensions/quick-reply/index.js b/public/scripts/extensions/quick-reply/index.js index 9bf3058b8..e47484f39 100644 --- a/public/scripts/extensions/quick-reply/index.js +++ b/public/scripts/extensions/quick-reply/index.js @@ -1,19 +1,48 @@ -import { saveSettingsDebounced } from "../../../script.js"; +import { saveSettingsDebounced, callPopup, getRequestHeaders } from "../../../script.js"; import { getContext, extension_settings } from "../../extensions.js"; import { initScrollHeight, resetScrollHeight } from "../../utils.js"; + export { MODULE_NAME }; const MODULE_NAME = 'quick-reply'; const UPDATE_INTERVAL = 1000; +let presets = []; +let selected_preset = ''; const defaultSettings = { - quickReplyEnabled: false, + quickReplyEnabled: true, numberOfSlots: 5, quickReplySlots: [], } -async function loadSettings() { +//method from worldinfo +async function updateQuickReplyPresetList() { + var result = await fetch("/getsettings", { + method: "POST", + headers: getRequestHeaders(), + body: JSON.stringify({}), + }); + + if (result.ok) { + var data = await result.json(); + presets = data.quickReplyPresets?.length ? data.quickReplyPresets : []; + console.log(presets) + $("#quickReplyPresets").find('option[value!=""]').remove(); + + + if (presets !== undefined) { + presets.forEach((item, i) => { + $("#quickReplyPresets").append(``); + }); + } + } +} + +async function loadSettings(type) { + if (type === 'init') { + await updateQuickReplyPresetList() + } if (Object.keys(extension_settings.quickReply).length === 0) { Object.assign(extension_settings.quickReply, defaultSettings); } @@ -111,6 +140,51 @@ async function moduleWorker() { if (extension_settings.quickReply.quickReplyEnabled === true) { $('#quickReplyBar').toggle(getContext().onlineStatus !== 'no_connection'); } + if (extension_settings.quickReply.selectedPreset) { + selected_preset = extension_settings.quickReply.selectedPreset; + } +} + +async function saveQuickReplyPreset() { + const name = await callPopup('Enter a name for the Quick Reply Preset:', 'input'); + + if (!name) { + return; + } + + const quickReplyPreset = { + name: name, + quickReplyEnabled: extension_settings.quickReply.quickReplyEnabled, + quickReplySlots: extension_settings.quickReply.quickReplySlots, + numberOfSlots: extension_settings.quickReply.numberOfSlots, + selectedPreset: name + } + + const response = await fetch('/savequickreply', { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify(quickReplyPreset) + }); + + if (response.ok) { + const quickReplyPresetIndex = presets.findIndex(x => x.name == name); + + if (quickReplyPresetIndex == -1) { + presets.push(quickReplyPreset); + const option = document.createElement('option'); + option.selected = true; + option.value = name; + option.innerText = name; + $('#quickReplyPresets').append(option); + } + else { + presets[quickReplyPresetIndex] = quickReplyPreset; + $(`#quickReplyPresets option[value="${name}"]`).attr('selected', true); + } + saveSettingsDebounced(); + } else { + toastr.warning('Failed to save Quick Reply Preset.') + } } async function onQuickReplyNumberOfSlotsInput() { @@ -178,6 +252,27 @@ function generateQuickReplyElements() { }); } +async function applyQuickReplyPreset(name) { + const quickReplyPreset = presets.find(x => x.name == name); + + if (!quickReplyPreset) { + console.log(`error, QR preset '${name}' not found`) + return; + } + + extension_settings.quickReply = quickReplyPreset; + extension_settings.quickReply.selectedPreset = name; + saveSettingsDebounced() + loadSettings('init') + addQuickReplyBar(); + moduleWorker(); + + $(`#quickReplyPresets option[value="${name}"]`).attr('selected', true); + + console.debug('QR Preset applied: ' + name); + //loadMovingUIState() +} + jQuery(async () => { moduleWorker(); @@ -190,11 +285,18 @@ jQuery(async () => {
- - +
+ +
+ + +
+ +