diff --git a/public/index.html b/public/index.html index be50b7029..0c0c37fc8 100644 --- a/public/index.html +++ b/public/index.html @@ -2496,10 +2496,10 @@ - + +
+
@@ -2698,7 +2700,7 @@
-
+
@@ -2743,23 +2745,24 @@
-

- AI Prompt Overrides - (Affects the generation) -

+

Prompt Overrides (For OpenAI/Claude APIs, and Instruct mode)

+ +
+ Insert {{original}} into either box to include the respective default prompt from system settings.
-

System Prompt

- +

Main Prompt

+
-

Post History Instructions

- +

Jailbreak

+
+

@@ -2769,25 +2772,30 @@

-
-

Creator's Name

- + Everything here is optional +
+
+

Created by

+ +
+
+

Character Version

+ +
-
-

Creator's Notes

- -
-
-

Creator's Tags

- -
-
-

Character Version

- +
+
+

Creator's Notes

+ +
+
+

Tags to Embed

+ +
- +

Personality summary @@ -3268,7 +3276,11 @@
Avatar
-
+
+
+
+
+
@@ -3460,4 +3472,4 @@ - \ No newline at end of file + diff --git a/public/script.js b/public/script.js index 551d23a7d..26185df1f 100644 --- a/public/script.js +++ b/public/script.js @@ -142,12 +142,13 @@ import { tag_map, tags, loadTagsSettings, - printTags, + printTagFilters, getTagsList, appendTagToList, createTagMapFromList, renameTagKey, importTags, + tag_filter_types, } from "./scripts/tags.js"; import { SECRET_KEYS, @@ -833,7 +834,8 @@ async function printCharacters() { $("#rm_print_characters_block").append(template); }); - printTags(); + printTagFilters(tag_filter_types.character); + printTagFilters(tag_filter_types.group_member); printGroups(); sortCharactersList(); favsToHotswap(); @@ -4651,7 +4653,7 @@ function selectKoboldGuiPreset() { async function saveSettings(type) { //console.log('Entering settings with name1 = '+name1); - eventSource.emit(event_types.SETTINGS_UPDATED); + return jQuery.ajax({ type: "POST", url: "/savesettings", @@ -4699,6 +4701,7 @@ async function saveSettings(type) { //processData: false, success: function (data) { //online_status = data.result; + eventSource.emit(event_types.SETTINGS_UPDATED); if (type == "change_name") { clearChat(); printMessages(); @@ -5519,11 +5522,12 @@ function openCharacterWorldPopup() { return; } - function onSelectCharacterWorld() { + async function onSelectCharacterWorld() { const value = $('.character_world_info_selector').find('option:selected').val(); const worldIndex = value !== '' ? Number(value) : NaN; const name = !isNaN(worldIndex) ? world_names[worldIndex] : ''; + const previousValue = $('#character_world').val(); $('#character_world').val(name); console.debug('Character world selected:', name); @@ -5531,7 +5535,23 @@ function openCharacterWorldPopup() { if (menu_type == 'create') { create_save.world = name; } else { - createOrEditCharacter(); + if (previousValue && !name) { + try { + // Dirty hack to remove embedded lorebook from character JSON data. + const data = JSON.parse($('#character_json_data').val()); + + if (data?.data?.character_book) { + data.data.character_book = undefined; + } + + $('#character_json_data').val(JSON.stringify(data)); + toastr.info('Embedded lorebook will be removed from this character.'); + } catch { + console.error('Failed to parse character JSON data.'); + } + } + + await createOrEditCharacter(); } setWorldInfoButtonClass(undefined, !!value); @@ -6301,6 +6321,7 @@ function importCharacter(file) { processData: false, success: async function (data) { if (data.file_name !== undefined) { + $('#character_search_bar').val('').trigger('input'); $("#rm_info_block").transition({ opacity: 0, duration: 0 }); var $prev_img = $("#avatar_div_div").clone(); $prev_img diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 6a0fa2509..8ef61a1ad 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -561,8 +561,8 @@ B: ${oldBottom}>> ${bottom} ---`) power_user.movingUIState[elmntName].margin = 'unset'; } else { - console.log('skipped unsetting margins') - console.log(oldTop, top, oldLeft, left) + console.debug('skipped unsetting margins') + //console.debug(oldTop, top, oldLeft, left) } saveSettingsDebounced(); diff --git a/public/scripts/extensions/expressions/index.js b/public/scripts/extensions/expressions/index.js index c7edfe266..b903e9717 100644 --- a/public/scripts/extensions/expressions/index.js +++ b/public/scripts/extensions/expressions/index.js @@ -161,7 +161,7 @@ async function visualNovelSetCharacterSprites(container, name, expression) { template.attr('data-avatar', avatar); template.find('.drag-grabber').attr('id', `expression-${avatar}header`); $('#visual-novel-wrapper').append(template); - dragElement(template[0]); + dragElement($(template[0])); template.toggleClass('hidden', noSprites); setImage(template.find('img'), defaultSpritePath || ''); const fadeInPromise = new Promise(resolve => { @@ -533,6 +533,11 @@ function drawSpritesList(character, labels, sprites) { $('.expression_settings').show(); $('#image_list').empty(); $('#image_list').data('name', character); + + if (!Array.isArray(labels)) { + return []; + } + labels.sort().forEach((item) => { const sprite = sprites.find(x => x.label == item); diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index ce673275d..5ab587d27 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -974,12 +974,19 @@ function select_group_chats(groupId, skipAnimation) { ? getThumbnailUrl('avatar', character.avatar) : default_avatar; const template = $("#group_member_template .group_member").clone(); + const isFav = character.fav || character.fav == 'true'; template.data("id", character.avatar); template.find(".avatar img").attr("src", avatar); template.find(".avatar img").attr("title", character.avatar); template.find(".ch_name").text(character.name); template.attr("chid", characters.indexOf(character)); - template.toggleClass('is_fav', character.fav || character.fav == 'true'); + template.find('.ch_fav').val(isFav); + template.toggleClass('is_fav', isFav); + + // Display inline tags + const tags = getTagsList(character.avatar); + const tagsElement = template.find('.tags'); + tags.forEach(tag => appendTagToList(tagsElement, tag, {})); if (!group) { template.find('[data-action="speak"]').hide(); @@ -999,7 +1006,6 @@ function select_group_chats(groupId, skipAnimation) { } sortGroupMembers("#rm_group_add_members .group_member"); - filterMembersByFavorites(false); const groupHasMembers = !!$("#rm_group_members").children().length; $("#rm_group_submit").prop("disabled", !groupHasMembers); @@ -1278,24 +1284,6 @@ async function createGroup() { } } -function toggleFilterByFavorites() { - filterMembersByFavorites(!fav_filter_on); -} - -function filterMembersByFavorites(value) { - fav_filter_on = value; - $('#group_fav_filter').toggleClass('fav_on', fav_filter_on); - - if (!fav_filter_on) { - $("#rm_group_add_members .group_member").removeClass('hiddenByFav'); - } else { - $("#rm_group_add_members .group_member").each(function () { - const isValidSearch = $(this).hasClass("is_fav"); - $(this).toggleClass('hiddenByFav', !isValidSearch); - }); - } -} - export async function createNewGroupChat(groupId) { const group = groups.find(x => x.id === groupId); @@ -1491,7 +1479,6 @@ function stopAutoModeGeneration() { jQuery(() => { $(document).on("click", ".group_select", selectGroup); $("#rm_group_filter").on("input", filterGroupMembers); - $("#group_fav_filter").on("click", toggleFilterByFavorites); $("#rm_group_submit").on("click", createGroup); $("#rm_group_scenario").on("click", setScenarioOverride); $("#rm_group_automode").on("input", function () { diff --git a/public/scripts/openai.js b/public/scripts/openai.js index a1295defc..26d2c69a9 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -990,8 +990,11 @@ function loadOpenAISettings(data, settings) { $('#stream_toggle').prop('checked', oai_settings.stream_openai); + $('#model_openai_select').val(oai_settings.openai_model); $(`#model_openai_select option[value="${oai_settings.openai_model}"`).attr('selected', true); + $('#model_claude_select').val(oai_settings.claude_model); $(`#model_claude_select option[value="${oai_settings.claude_model}"`).attr('selected', true); + $('#model_windowai_select').val(oai_settings.windowai_model); $(`#model_windowai_select option[value="${oai_settings.windowai_model}"`).attr('selected', true); $('#openai_max_context').val(oai_settings.openai_max_context); $('#openai_max_context_counter').text(`${oai_settings.openai_max_context}`); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 3b6648165..427875940 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -929,68 +929,43 @@ async function saveTheme() { } function resetMovablePanels() { - document.getElementById("sheld").style.top = ''; - document.getElementById("sheld").style.left = ''; - document.getElementById("sheld").style.bottom = ''; - document.getElementById("sheld").style.right = ''; - document.getElementById("sheld").style.height = ''; - document.getElementById("sheld").style.width = ''; - document.getElementById("sheld").style.margin = ''; + const panelIds = [ + 'sheld', + 'left-nav-panel', + 'right-nav-panel', + 'WorldInfo', + 'floatingPrompt', + 'expression-holder', + ]; + const panelStyles = ['top', 'left', 'right', 'bottom', 'height', 'width', 'margin',]; - document.getElementById("left-nav-panel").style.top = ''; - document.getElementById("left-nav-panel").style.left = ''; - document.getElementById("left-nav-panel").style.height = ''; - document.getElementById("left-nav-panel").style.width = ''; - document.getElementById("left-nav-panel").style.margin = ''; + panelIds.forEach((id) => { + const panel = document.getElementById(id); - document.getElementById("right-nav-panel").style.top = ''; - document.getElementById("right-nav-panel").style.left = ''; - document.getElementById("right-nav-panel").style.right = ''; - document.getElementById("right-nav-panel").style.height = ''; - document.getElementById("right-nav-panel").style.width = ''; - document.getElementById("right-nav-panel").style.margin = ''; + if (panel) { + panelStyles.forEach((style) => { + panel.style[style] = ''; + }); + } + }); - document.getElementById("WorldInfo").style.top = ''; - document.getElementById("WorldInfo").style.left = ''; - document.getElementById("WorldInfo").style.right = ''; - document.getElementById("WorldInfo").style.bottom = ''; - document.getElementById("WorldInfo").style.height = ''; - document.getElementById("WorldInfo").style.width = ''; - document.getElementById("WorldInfo").style.margin = ''; - - document.getElementById("floatingPrompt").style.top = ''; - document.getElementById("floatingPrompt").style.left = ''; - document.getElementById("floatingPrompt").style.right = ''; - document.getElementById("floatingPrompt").style.bottom = ''; - document.getElementById("floatingPrompt").style.height = ''; - document.getElementById("floatingPrompt").style.width = ''; - document.getElementById("floatingPrompt").style.margin = ''; - - if ($("#expression-holder")) { - document.getElementById("expression-holder").style.top = ''; - document.getElementById("expression-holder").style.left = ''; - document.getElementById("expression-holder").style.right = ''; - document.getElementById("expression-holder").style.bottom = ''; - document.getElementById("expression-holder").style.height = ''; - document.getElementById("expression-holder").style.width = ''; - document.getElementById("expression-holder").style.margin = ''; + const zoomedAvatar = document.querySelector('.zoomed_avatar'); + if (zoomedAvatar) { + panelStyles.forEach((style) => { + zoomedAvatar.style[style] = ''; + }); } - if ($(".zoomed_avatar")) { - $(".zoomed_avatar").css('top', ''); - $(".zoomed_avatar").css('left', ''); - $(".zoomed_avatar").css('right', ''); - $(".zoomed_avatar").css('bottom', ''); - $(".zoomed_avatar").css('width', ''); - $(".zoomed_avatar").css('height', ''); - $(".zoomed_avatar").css('margin', ''); - } - - $('*[data-dragged="true"]').removeAttr('data-dragged'); - power_user.movingUIState = {} + $('[data-dragged="true"]').removeAttr('data-dragged'); + power_user.movingUIState = {}; saveSettingsDebounced(); eventSource.emit(event_types.MOVABLE_PANELS_RESET); + + eventSource.once(event_types.SETTINGS_UPDATED, () => { + toastr.success('Panel positions reset'); + }); + } function doNewChat() { diff --git a/public/scripts/tags.js b/public/scripts/tags.js index 7a3e53981..3888c9f3b 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -14,7 +14,7 @@ export { tags, tag_map, loadTagsSettings, - printTags, + printTagFilters, isElementTagged, getTagsList, appendTagToList, @@ -26,12 +26,28 @@ export { const random_id = () => Math.round(Date.now() * Math.random()).toString(); const TAG_LOGIC_AND = true; // switch to false to use OR logic for combining tags const CHARACTER_SELECTOR = '#rm_print_characters_block > div'; +const GROUP_MEMBER_SELECTOR = '#rm_group_add_members > div'; +const CHARACTER_FILTER_SELECTOR = '#rm_characters_block .rm_tag_filter'; +const GROUP_FILTER_SELECTOR = '#rm_group_chats_block .rm_tag_filter'; + +function getCharacterSelector(listSelector) { + if ($(listSelector).is(GROUP_FILTER_SELECTOR)) { + return GROUP_MEMBER_SELECTOR; + } + + return CHARACTER_SELECTOR; +} + +export const tag_filter_types = { + character: 0, + group_member: 1, +}; const ACTIONABLE_TAGS = { - FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star' }, - GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users' }, - HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags' }, + FAV: { id: 1, name: 'Show only favorites', color: 'rgba(255, 255, 0, 0.5)', action: applyFavFilter, icon: 'fa-solid fa-star', class: 'filterByFavorites' }, + GROUP: { id: 0, name: 'Show only groups', color: 'rgba(100, 100, 100, 0.5)', action: filterByGroups, icon: 'fa-solid fa-users', class: 'filterByGroups' }, + HINT: { id: 3, name: 'Show Tag List', color: 'rgba(150, 100, 100, 0.5)', action: onTagListHintClick, icon: 'fa-solid fa-tags', class: 'showTagList' }, } const InListActionable = { @@ -50,14 +66,14 @@ const DEFAULT_TAGS = [ let tags = []; let tag_map = {}; -function applyFavFilter() { +function applyFavFilter(characterSelector) { const isSelected = $(this).hasClass('selected'); const displayFavoritesOnly = !isSelected; $(this).toggleClass('selected', displayFavoritesOnly); - $(CHARACTER_SELECTOR).removeClass('hiddenByFav'); + $(characterSelector).removeClass('hiddenByFav'); - $(CHARACTER_SELECTOR).each(function () { + $(characterSelector).each(function () { if (displayFavoritesOnly) { if ($(this).find(".ch_fav").length !== 0) { const shouldBeDisplayed = $(this).find(".ch_fav").val().toLowerCase().includes(true); @@ -69,13 +85,13 @@ function applyFavFilter() { updateVisibleDivs('#rm_print_characters_block', true); } -function filterByGroups() { +function filterByGroups(characterSelector) { const isSelected = $(this).hasClass('selected'); const displayGroupsOnly = !isSelected; $(this).toggleClass('selected', displayGroupsOnly); - $(CHARACTER_SELECTOR).removeClass('hiddenByGroup'); + $(characterSelector).removeClass('hiddenByGroup'); - $(CHARACTER_SELECTOR).each((_, element) => { + $(characterSelector).each((_, element) => { $(element).toggleClass('hiddenByGroup', displayGroupsOnly && !$(element).hasClass('group_select')); }); updateVisibleDivs('#rm_print_characters_block', true); @@ -197,7 +213,8 @@ function selectTag(event, ui, listSelector) { appendTagToList(getInlineListSelector(), tag, { removable: false }); addTagToMap(tag.id); saveSettingsDebounced(); - printTags(); + printTagFilters(tag_filter_types.character); + printTagFilters(tag_filter_types.group_member); // need to return false to keep the input clear return false; @@ -264,6 +281,8 @@ function appendTagToList(listElement, tag, { removable, selectable, action }) { return; } + const characterSelector = getCharacterSelector($(listElement)); + let tagElement = $('#tag_template .tag').clone(); tagElement.attr('id', tag.id); @@ -274,16 +293,20 @@ function appendTagToList(listElement, tag, { removable, selectable, action }) { const removeButton = tagElement.find(".tag_remove"); removable ? removeButton.show() : removeButton.hide(); + if (tag.class) { + tagElement.addClass(tag.class); + } + if (tag.icon) { tagElement.find('.tag_name').text('').attr('title', tag.name).addClass(tag.icon); } if (selectable) { - tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement)); + tagElement.on('click', () => onTagFilterClick.bind(tagElement)(listElement, characterSelector)); } if (action) { - tagElement.on('click', () => action.bind(tagElement)()); + tagElement.on('click', () => action.bind(tagElement)(characterSelector)); tagElement.addClass('actionable'); } if (action && tag.id === 2) { @@ -293,7 +316,7 @@ function appendTagToList(listElement, tag, { removable, selectable, action }) { $(listElement).append(tagElement); } -function onTagFilterClick(listElement) { +function onTagFilterClick(listElement, characterSelector) { if ($(this).hasClass('selected')) { $(this).removeClass('selected'); $(this).addClass('excluded'); @@ -307,7 +330,7 @@ function onTagFilterClick(listElement) { const tagIds = [...($(listElement).find(".tag.selected:not(.actionable)").map((_, el) => $(el).attr("id")))]; const excludedTagIds = [...($(listElement).find(".tag.excluded:not(.actionable)").map((_, el) => $(el).attr("id")))]; - $(CHARACTER_SELECTOR).each((_, element) => applyFilterToElement(tagIds, excludedTagIds, element)); + $(characterSelector).each((_, element) => applyFilterToElement(tagIds, excludedTagIds, element)); updateVisibleDivs('#rm_print_characters_block', true); } @@ -330,7 +353,7 @@ function applyFilterToElement(tagIds, excludedTagIds, element) { function isElementTagged(element, tagId) { const isGroup = $(element).hasClass('group_select'); - const isCharacter = $(element).hasClass('character_select'); + const isCharacter = $(element).hasClass('character_select') || $(element).hasClass('group_member'); const idAttr = isGroup ? 'grid' : 'chid'; const elementId = $(element).attr(idAttr); const lookupValue = isCharacter ? characters[elementId].avatar : elementId; @@ -338,13 +361,13 @@ function isElementTagged(element, tagId) { return isTagged; } -function clearTagsFilter() { - $('#rm_tag_filter .tag').removeClass('selected'); - $(CHARACTER_SELECTOR).removeClass('hiddenByTag'); +function clearTagsFilter(characterSelector) { + $('.rm_tag_filter .tag').removeClass('selected'); + $(characterSelector).removeClass('hiddenByTag'); } -function printTags() { - const FILTER_SELECTOR = '#rm_tag_filter'; +function printTagFilters(type = tag_filter_types.character) { + const FILTER_SELECTOR = type === tag_filter_types.character ? CHARACTER_FILTER_SELECTOR : GROUP_FILTER_SELECTOR; const selectedTagIds = [...($(FILTER_SELECTOR).find(".tag.selected").map((_, el) => $(el).attr("id")))]; $(FILTER_SELECTOR).empty(); const characterTagIds = Object.values(tag_map).flat(); @@ -378,7 +401,8 @@ function onTagRemoveClick(event) { removeTagFromMap(tagId); $(`${getInlineListSelector()} .tag[id="${tagId}"]`).remove(); - printTags(); + printTagFilters(tag_filter_types.character); + printTagFilters(tag_filter_types.group_member); saveSettingsDebounced(); } @@ -398,6 +422,8 @@ function onCharacterCreateClick() { function onGroupCreateClick() { $("#groupTagList").empty(); + printTagFilters(tag_filter_types.character); + printTagFilters(tag_filter_types.group_member); } export function applyTagsOnCharacterSelect() { @@ -419,6 +445,8 @@ function applyTagsOnGroupSelect() { const tags = getTagsList(key); $("#groupTagList").empty(); + printTagFilters(tag_filter_types.character); + printTagFilters(tag_filter_types.group_member); for (const tag of tags) { appendTagToList("#groupTagList", tag, { removable: true }); diff --git a/public/style.css b/public/style.css index 39e127b41..1ca17a69c 100644 --- a/public/style.css +++ b/public/style.css @@ -2433,7 +2433,7 @@ input[type="range"]::-webkit-slider-thumb { .mes_buttons .mes_edit, .mes_buttons .mes_bookmark, .extraMesButtonsHint, -#tagListHint, +.tagListHint, .extraMesButtons div { cursor: pointer; transition: 0.3s ease-in-out; @@ -2444,7 +2444,7 @@ input[type="range"]::-webkit-slider-thumb { .mes_buttons .mes_edit:hover, .mes_buttons .mes_bookmark:hover, .extraMesButtonsHint:hover, -#tagListHint:hover, +.tagListHint:hover, .extraMesButtons div:hover { opacity: 1; } @@ -2546,7 +2546,7 @@ input[type="range"]::-webkit-slider-thumb { #character_popup { display: none; - background-color: var(--black30a); + background-color: var(--SmartThemeBlurTintColor); backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)*2)); -webkit-backdrop-filter: blur(calc(var(--SmartThemeBlurStrength)*2)); @@ -2838,10 +2838,13 @@ h5 { text-shadow: none !important; } -#rm_tag_filter .tag:not(.actionable) { +.rm_tag_filter .tag:not(.actionable) { display: none; } +#rm_group_chats_block .tag.filterByGroups { + display: none; +} .tag.actionable { border-radius: 50%; @@ -2855,7 +2858,7 @@ h5 { align-items: center; } -#tagListHint { +.tagListHint { align-self: center; display: flex; margin-right: 10px; @@ -2927,7 +2930,7 @@ h5 { margin: 5px; } -#rm_tag_filter .tag { +.rm_tag_filter .tag { cursor: pointer; opacity: 0.6; filter: brightness(0.8); @@ -3151,15 +3154,17 @@ body .ui-widget-content li:hover { border-radius: 10px; } -.group_member .ch_name { +.group_member .group_member_name { flex-grow: 1; margin-left: 10px; overflow: hidden; text-overflow: ellipsis; width: calc(100% - 110px); display: flex; - align-items: center; gap: 5px; + height: 100%; + flex-direction: column; + justify-content: center; } .group_member_icon .flex-container { @@ -4452,7 +4457,8 @@ body.waifuMode .zoomed_avatar { } .world_entry_thin_controls, - #persona-management-block { + #persona-management-block, + #character_popup .flex-container { flex-direction: column; } @@ -4935,4 +4941,4 @@ body.waifuMode .zoomed_avatar { background-color: var(--SmartThemeBlurTintColor); text-align: center; line-height: 14px; -} \ No newline at end of file +} diff --git a/server.js b/server.js index 4cc5b24ec..c69ba014d 100644 --- a/server.js +++ b/server.js @@ -4025,7 +4025,7 @@ async function downloadChubCharacter(id) { } const buffer = await result.buffer(); - const fileName = result.headers.get('content-disposition').split('filename=')[1]; + const fileName = result.headers.get('content-disposition')?.split('filename=')[1] || `${sanitize(id)}.png`; const fileType = result.headers.get('content-type'); return { buffer, fileName, fileType };