diff --git a/public/css/mobile-styles.css b/public/css/mobile-styles.css index d62534876..ebcd5eda2 100644 --- a/public/css/mobile-styles.css +++ b/public/css/mobile-styles.css @@ -1,5 +1,9 @@ /*will apply to anything 1000px or less. this catches ipads, horizontal phones, and vertical phones)*/ @media screen and (max-width: 1000px) { + #send_form.compact #leftSendForm, #send_form.compact #rightSendForm { + flex-wrap: nowrap; + width: unset; + } .bg_button { font-size: 15px; diff --git a/public/index.html b/public/index.html index 97d8b1bbb..c882cf64b 100644 --- a/public/index.html +++ b/public/index.html @@ -2786,9 +2786,12 @@
-
@@ -2982,6 +2985,10 @@ Message Token Count +
- +
`; return html; } diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 57d54a036..7108a9914 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -110,10 +110,18 @@ export const group_generation_mode = { APPEND: 1, }; +const DEFAULT_AUTO_MODE_DELAY = 5; + export const groupCandidatesFilter = new FilterHelper(debounce(printGroupCandidates, 100)); -setInterval(groupChatAutoModeWorker, 5000); +let autoModeWorker = null; const saveGroupDebounced = debounce(async (group, reload) => await _save(group, reload), 500); +function setAutoModeWorker() { + clearInterval(autoModeWorker); + const autoModeDelay = groups.find(x => x.id === selected_group)?.auto_mode_delay ?? DEFAULT_AUTO_MODE_DELAY; + autoModeWorker = setInterval(groupChatAutoModeWorker, autoModeDelay * 1000); +} + async function _save(group, reload = true) { await fetch('/api/groups/edit', { method: 'POST', @@ -1035,6 +1043,15 @@ async function onGroupGenerationModeInput(e) { } } +async function onGroupAutoModeDelayInput(e) { + if (openGroupId) { + let _thisGroup = groups.find((x) => x.id == openGroupId); + _thisGroup.auto_mode_delay = Number(e.target.value); + await editGroup(openGroupId, false, false); + setAutoModeWorker(); + } +} + async function onGroupNameInput() { if (openGroupId) { let _thisGroup = groups.find((x) => x.id == openGroupId); @@ -1231,6 +1248,7 @@ function select_group_chats(groupId, skipAnimation) { $('#rm_group_submit').prop('disabled', !groupHasMembers); $('#rm_group_allow_self_responses').prop('checked', group && group.allow_self_responses); $('#rm_group_hidemutedsprites').prop('checked', group && group.hideMutedSprites); + $('#rm_group_automode_delay').val(group?.auto_mode_delay ?? DEFAULT_AUTO_MODE_DELAY); // bottom buttons if (openGroupId) { @@ -1249,6 +1267,7 @@ function select_group_chats(groupId, skipAnimation) { } updateFavButtonState(group?.fav ?? false); + setAutoModeWorker(); // top bar if (group) { @@ -1441,6 +1460,7 @@ async function createGroup() { let allowSelfResponses = !!$('#rm_group_allow_self_responses').prop('checked'); let activationStrategy = Number($('#rm_group_activation_strategy').find(':selected').val()) ?? group_activation_strategy.NATURAL; let generationMode = Number($('#rm_group_generation_mode').find(':selected').val()) ?? group_generation_mode.SWAP; + let autoModeDelay = Number($('#rm_group_automode_delay').val()) ?? DEFAULT_AUTO_MODE_DELAY; const members = newGroupMembers; const memberNames = characters.filter(x => members.includes(x.avatar)).map(x => x.name).join(', '); @@ -1469,6 +1489,7 @@ async function createGroup() { fav: fav_grp_checked, chat_id: chatName, chats: chats, + auto_mode_delay: autoModeDelay, }), }); @@ -1741,6 +1762,7 @@ jQuery(() => { $('#rm_group_allow_self_responses').on('input', onGroupSelfResponsesClick); $('#rm_group_activation_strategy').on('change', onGroupActivationStrategyInput); $('#rm_group_generation_mode').on('change', onGroupGenerationModeInput); + $('#rm_group_automode_delay').on('input', onGroupAutoModeDelayInput); $('#group_avatar_button').on('input', uploadGroupAvatar); $('#rm_group_restore_avatar').on('click', restoreGroupAvatar); $(document).on('click', '.group_member .right_menu_button', onGroupActionClick); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 8eba7a939..7153bfd2d 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -232,6 +232,7 @@ let power_user = { aux_field: 'character_version', restore_user_input: true, reduced_motion: false, + compact_input_area: true, }; let themes = []; @@ -273,6 +274,7 @@ const storage_keys = { enableZenSliders: 'enableZenSliders', enableLabMode: 'enableLabMode', reduced_motion: 'reduced_motion', + compact_input_area: 'compact_input_area', }; const contextControls = [ @@ -450,6 +452,13 @@ function switchReducedMotion() { $('#reduced_motion').prop('checked', power_user.reduced_motion); } +function switchCompactInputArea() { + const value = localStorage.getItem(storage_keys.compact_input_area); + power_user.compact_input_area = value === null ? true : value == 'true'; + $('#send_form').toggleClass('compact', power_user.compact_input_area); + $('#compact_input_area').prop('checked', power_user.compact_input_area); +} + var originalSliderValues = []; async function switchLabMode() { @@ -547,7 +556,7 @@ async function CreateZenSliders(elmnt) { var sliderMax = Number(originalSlider.attr('max')); var sliderValue = originalSlider.val(); var sliderRange = sliderMax - sliderMin; - var numSteps = 10; + var numSteps = 20; var decimals = 2; var offVal, allVal; var stepScale; @@ -1246,6 +1255,15 @@ async function applyTheme(name) { action: async () => { localStorage.setItem(storage_keys.reduced_motion, String(power_user.reduced_motion)); $('#reduced_motion').prop('checked', power_user.reduced_motion); + switchReducedMotion(); + }, + }, + { + key: 'compact_input_area', + action: async () => { + localStorage.setItem(storage_keys.compact_input_area, String(power_user.compact_input_area)); + $('#compact_input_area').prop('checked', power_user.compact_input_area); + switchCompactInputArea(); }, }, ]; @@ -1504,6 +1522,7 @@ function loadPowerUserSettings(settings, data) { $(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop('selected', true); switchReducedMotion(); + switchCompactInputArea(); reloadMarkdownProcessor(power_user.render_formulas); loadInstructMode(data); loadContextSettings(); @@ -1893,11 +1912,26 @@ function sortEntitiesList(entities) { }); } -async function saveTheme() { - const name = await callPopup('Enter a theme preset name:', 'input'); +/** + * Updates the current UI theme file. + */ +async function updateTheme() { + await saveTheme(power_user.theme); + toastr.success('Theme saved.'); +} - if (!name) { - return; +/** + * Saves the current theme to the server. + * @param {string|undefined} name Theme name. If undefined, a popup will be shown to enter a name. + * @returns {Promise} A promise that resolves when the theme is saved. + */ +async function saveTheme(name = undefined) { + if (typeof name !== 'string') { + name = await callPopup('Enter a theme preset name:', 'input', power_user.theme); + + if (!name) { + return; + } } const theme = { @@ -1932,6 +1966,8 @@ async function saveTheme() { hotswap_enabled: power_user.hotswap_enabled, custom_css: power_user.custom_css, bogus_folders: power_user.bogus_folders, + reduced_motion: power_user.reduced_motion, + compact_input_area: power_user.compact_input_area, }; const response = await fetch('/savetheme', { @@ -2804,7 +2840,8 @@ $(document).ready(() => { saveSettingsDebounced(); }); - $('#ui-preset-save-button').on('click', saveTheme); + $('#ui-preset-save-button').on('click', () => saveTheme()); + $('#ui-preset-update-button').on('click', () => updateTheme()); $('#movingui-preset-save-button').on('click', saveMovingUI); $('#never_resize_avatars').on('input', function () { @@ -3151,6 +3188,13 @@ $(document).ready(() => { saveSettingsDebounced(); }); + $('#compact_input_area').on('input', function () { + power_user.compact_input_area = !!$(this).prop('checked'); + localStorage.setItem(storage_keys.compact_input_area, String(power_user.compact_input_area)); + switchCompactInputArea(); + saveSettingsDebounced(); + }); + $(document).on('click', '#debug_table [data-debug-function]', function () { const functionId = $(this).data('debug-function'); const functionRecord = debug_functions.find(f => f.functionId === functionId); diff --git a/src/endpoints/groups.js b/src/endpoints/groups.js index d7341ff04..f8a117e84 100644 --- a/src/endpoints/groups.js +++ b/src/endpoints/groups.js @@ -73,6 +73,7 @@ router.post('/create', jsonParser, (request, response) => { fav: request.body.fav, chat_id: request.body.chat_id ?? id, chats: request.body.chats ?? [id], + auto_mode_delay: request.body.auto_mode_delay ?? 5, }; const pathToFile = path.join(DIRECTORIES.groups, `${id}.json`); const fileData = JSON.stringify(groupMetadata);