diff --git a/public/index.html b/public/index.html index a9e3b7efc..961a23b8b 100644 --- a/public/index.html +++ b/public/index.html @@ -3394,10 +3394,13 @@
WEBP
-
-
- +
+
+
+ +
+
@@ -3446,4 +3449,4 @@ - + \ No newline at end of file diff --git a/public/script.js b/public/script.js index 9ca6ada19..533d8597a 100644 --- a/public/script.js +++ b/public/script.js @@ -1,4 +1,4 @@ -import { humanizedDateTime, favsToHotswap, getMessageTimeStamp } from "./scripts/RossAscends-mods.js"; +import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile } from "./scripts/RossAscends-mods.js"; import { encode } from "../scripts/gpt-2-3-tokenizer/mod.js"; import { GPT3BrowserTokenizer } from "../scripts/gpt-3-tokenizer/gpt3-tokenizer.js"; import { @@ -72,6 +72,7 @@ import { formatInstructStoryString, formatInstructModePrompt, persona_description_positions, + loadMovingUIState, } from "./scripts/power-user.js"; import { @@ -1013,6 +1014,10 @@ function clearChat() { count_view_mes = 0; extension_prompts = {}; $("#chat").children().remove(); + if ($('.zoomed_avatar[forChar]').length) { + console.debug('saw avatars to remove') + $('.zoomed_avatar[forChar]').remove(); + } else { console.debug('saw no avatars') } itemizedPrompts = []; } @@ -4409,7 +4414,7 @@ function lockUserNameToChat() { { timeOut: 10000, extendedTimeOut: 20000, }, ); power_user.personas[user_avatar] = name1; - power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.BEFORE_CHAR }; + power_user.persona_descriptions[user_avatar] = { description: '', position: persona_description_positions.BEFORE_CHAR }; } chat_metadata['persona'] = user_avatar; @@ -6351,7 +6356,8 @@ $(document).ready(function () { }); $(document).click(event => { if ($(':focus').attr('id') !== 'send_textarea') { - if (!$(event.target.id).is("#options_button, #send_but, #send_textarea, #option_regenerate")) { + var validIDs = ["options_button", "send_but", "send_textarea", "option_regenerate"]; + if ($(event.target).attr('id') !== validIDs) { S_TAFocused = false; S_TAPreviouslyFocused = false; } @@ -6762,13 +6768,13 @@ $(document).ready(function () { $("#form_create").submit(createOrEditCharacter); $("#delete_button").on('click', function () { - popup_type = "del_ch"; - callPopup(` + popup_type = "del_ch"; + callPopup(`

Delete the character?

THIS IS PERMANENT!

THIS WILL ALSO DELETE ALL
OF THE CHARACTER'S CHAT FILES.

` - ); + ); }); $("#rm_info_button").on('click', function () { @@ -7548,8 +7554,8 @@ $(document).ready(function () { } }); $("#export_button").on('click', function (e) { - $('#export_format_popup').toggle(); - exportPopper.update(); + $('#export_format_popup').toggle(); + exportPopper.update(); }); $(document).on('click', '.export_format', async function () { const format = $(this).data('format'); @@ -7634,16 +7640,16 @@ $(document).ready(function () { $("#dupe_button").click(async function () { - const body = { avatar_url: characters[this_chid].avatar }; - const response = await fetch('/dupecharacter', { - method: 'POST', - headers: getRequestHeaders(), - body: JSON.stringify(body), - }); - if (response.ok) { - toastr.success("Character Duplicated"); - getCharacters(); - } + const body = { avatar_url: characters[this_chid].avatar }; + const response = await fetch('/dupecharacter', { + method: 'POST', + headers: getRequestHeaders(), + body: JSON.stringify(body), + }); + if (response.ok) { + toastr.success("Character Duplicated"); + getCharacters(); + } }); $(document).on("click", ".select_chat_block, .bookmark_link, .mes_bookmark", async function () { @@ -7682,12 +7688,15 @@ $(document).ready(function () { $('.drawer-toggle').click(function () { var icon = $(this).find('.drawer-icon'); var drawer = $(this).parent().find('.drawer-content'); + if (drawer.hasClass('resizing')) { return } var drawerWasOpenAlready = $(this).parent().find('.drawer-content').hasClass('openDrawer'); let targetDrawerID = $(this).parent().find('.drawer-content').attr('id'); const pinnedDrawerClicked = drawer.hasClass('pinnedOpen'); - if (!drawerWasOpenAlready) { - $('.openDrawer').not('.pinnedOpen').slideToggle(200, "swing"); + if (!drawerWasOpenAlready) { //to open the drawer + $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, "swing", function () { + $(this).closest('.drawer-content').removeClass('resizing'); + }); $('.openIcon').toggleClass('closedIcon openIcon'); $('.openDrawer').not('.pinnedOpen').toggleClass('closedDrawer openDrawer'); icon.toggleClass('openIcon closedIcon'); @@ -7695,29 +7704,36 @@ $(document).ready(function () { //console.log(targetDrawerID); if (targetDrawerID === 'right-nav-panel') { - $(this).closest('.drawer').find('.drawer-content').slideToggle({ + $(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggle({ duration: 200, easing: "swing", start: function () { jQuery(this).css('display', 'flex'); }, complete: function () { + $(this).closest('.drawer-content').removeClass('resizing'); $("#rm_print_characters_block").trigger("scroll"); } }) } else { - $(this).closest('.drawer').find('.drawer-content').slideToggle(200, "swing"); + $(this).closest('.drawer').find('.drawer-content').addClass('resizing').slideToggle(200, "swing", function () { + $(this).closest('.drawer-content').removeClass('resizing'); + }); } - } else if (drawerWasOpenAlready) { + } else if (drawerWasOpenAlready) { //to close icon.toggleClass('closedIcon openIcon'); if (pinnedDrawerClicked) { - $(drawer).slideToggle(200, "swing"); + $(drawer).addClass('resizing').slideToggle(200, "swing", function () { + $(this).removeClass('resizing'); + }); } else { - $('.openDrawer').not('.pinnedOpen').slideToggle(200, "swing"); + $('.openDrawer').not('.pinnedOpen').addClass('resizing').slideToggle(200, "swing", function () { + $(this).closest('.drawer-content').removeClass('resizing'); + }); } drawer.toggleClass('closedDrawer openDrawer'); @@ -7772,22 +7788,44 @@ $(document).ready(function () { }); $(document).on('click', '.mes .avatar', function () { + + console.log(isMobile()); + console.log($('body').hasClass('waifuMode')); + + if (isMobile() === true && !$('body').hasClass('waifuMode')) { + console.debug('saw mobile regular mode, returning'); + return; + } else { console.debug('saw valid env for zoomed display') } + let thumbURL = $(this).children('img').attr('src'); let charsPath = '/characters/' let targetAvatarImg = thumbURL.substring(thumbURL.lastIndexOf("=") + 1); + let charname = targetAvatarImg.replace('.png', ''); let avatarSrc = isDataURL(thumbURL) ? thumbURL : charsPath + targetAvatarImg; - console.log(avatarSrc); - if ($(this).parent().parent().attr('is_user') == 'true') { //handle user avatars - $("#zoomed_avatar").attr('src', thumbURL); - } else if ($(this).parent().parent().attr('is_system') == 'true') { //handle system avatars - $("#zoomed_avatar").attr('src', thumbURL); - } else if ($(this).parent().parent().attr('is_user') == 'false') { //handle char avatars - $("#zoomed_avatar").attr('src', avatarSrc); - } - $('#avatar_zoom_popup').toggle(); + if ($(`.zoomed_avatar[forChar="${charname}"]`).length) { + console.debug('removing container as it already existed') + $(`.zoomed_avatar[forChar="${charname}"]`).remove(); + } else { + console.debug('making new container from template') + const template = $('#zoomed_avatar_template').html(); + const newElement = $(template); + newElement.attr('forChar', charname); + newElement.attr('id', `zoomFor_${charname}`); + newElement.find('.drag-grabber').attr('id', `zoomFor_${charname}header`); - //} else { return; } + $('body').append(newElement); + if ($(this).parent().parent().attr('is_user') == 'true') { //handle user avatars + $(`.zoomed_avatar[forChar="${charname}"] img`).attr('src', thumbURL); + } else if ($(this).parent().parent().attr('is_system') == 'true') { //handle system avatars + $(`.zoomed_avatar[forChar="${charname}"] img`).attr('src', thumbURL); + } else if ($(this).parent().parent().attr('is_user') == 'false') { //handle char avatars + $(`.zoomed_avatar[forChar="${charname}"] img`).attr('src', avatarSrc); + } + loadMovingUIState(); + $(`.zoomed_avatar[forChar="${charname}"]`).css('display', 'block'); + dragElement(newElement) + } }); $(document).on('click', '#OpenAllWIEntries', function () { diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 775d4ba22..af4d49ee3 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -473,63 +473,97 @@ function OpenNavPanels() { // Make the DIV element draggable: -dragElement(document.getElementById("sheld")); -dragElement(document.getElementById("left-nav-panel")); -dragElement(document.getElementById("right-nav-panel")); -dragElement(document.getElementById("avatar_zoom_popup")); -dragElement(document.getElementById("WorldInfo")); + + +// SECOND UPDATE AIMING FOR MUTATIONS ONLY export function dragElement(elmnt) { - var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - var draggableHeight, draggableWidth, draggableTop, draggableLeft; - var elmntName = $(elmnt).attr('id'); - - if (document.getElementById(elmnt.id + "header")) { //ex: id="sheldheader" - // if present, the header is where you move the DIV from, but this overrides everything else: - document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown; + var height, width, top, left; + var elmntName = elmnt.attr('id'); + const elmntNameEscaped = $.escapeSelector(elmntName); + const elmntHeader = $(`#${elmntNameEscaped}header`); + if (elmntHeader.length) { + elmntHeader.off('mousedown').on('mousedown', (e) => { + dragMouseDown(e); + }); } else { - // otherwise, move the DIV from anywhere inside the DIV, b: - elmnt.onmousedown = dragMouseDown; + elmnt.off('mousedown').on('mousedown', dragMouseDown); } + const observer = new MutationObserver((mutations) => { + const target = mutations[0].target; + if (!$(target).is(':visible') + || $(target).hasClass('resizing') + || Number((String(target.height).replace('px', ''))) < 50 + || Number((String(target.width).replace('px', ''))) < 50 + || isMobile() === true + ) { return } + + const style = getComputedStyle(target); + console.log(style.top, style.left) + height = target.offsetHeight; + width = target.offsetWidth; + top = parseInt(style.top); + left = parseInt(style.left); + + /* console.log(` + height=${height}, + width=${width}, + top=${top}, + left=${left}`); */ + + if (!power_user.movingUIState[elmntName]) { + power_user.movingUIState[elmntName] = {}; + } + + power_user.movingUIState[elmntName].top = top; + power_user.movingUIState[elmntName].left = left; + power_user.movingUIState[elmntName].width = width; + power_user.movingUIState[elmntName].height = height; + power_user.movingUIState[elmntName].right = 'unset'; + power_user.movingUIState[elmntName].bottom = 'unset'; + power_user.movingUIState[elmntName].margin = 'unset'; + saveSettingsDebounced(); + saveSettingsDebounced(); + + + // Check if the element header exists and set the listener on the grabber + if (elmntHeader.length) { + elmntHeader.off('mousedown').on('mousedown', (e) => { + dragMouseDown(e); + }); + } else { + elmnt.off('mousedown').on('mousedown', dragMouseDown); + } + }); + + observer.observe(elmnt.get(0), { attributes: true, attributeFilter: ['style'] }); + function dragMouseDown(e) { - //console.log(e); - e = e || window.event; - e.preventDefault(); - // get the mouse cursor position at startup: - pos3 = e.clientX; //mouse X at click - pos4 = e.clientY; //mouse Y at click - document.onmouseup = closeDragElement; - // call a function whenever the cursor moves: - document.onmousemove = elementDrag; + if (e) { + e.preventDefault(); + pos3 = e.clientX; //mouse X at click + pos4 = e.clientY; //mouse Y at click + } + $(document).on('mouseup', closeDragElement); + $(document).on('mousemove', elementDrag); } function elementDrag(e) { - //disable scrollbars when dragging to prevent jitter $("body").css("overflow", "hidden"); if (!power_user.movingUIState[elmntName]) { power_user.movingUIState[elmntName] = {}; } - //get window size let winWidth = window.innerWidth; let winHeight = window.innerHeight; - - //get necessary data for calculating element footprint - draggableHeight = parseInt(getComputedStyle(elmnt).getPropertyValue('height').slice(0, -2)); - draggableWidth = parseInt(getComputedStyle(elmnt).getPropertyValue('width').slice(0, -2)); - draggableTop = parseInt(getComputedStyle(elmnt).getPropertyValue('top').slice(0, -2)); - draggableLeft = parseInt(getComputedStyle(elmnt).getPropertyValue('left').slice(0, -2)); - let sheldWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--sheldWidth').slice(0, -2)); + let sheldWidth = parseInt($('html').css('--sheldWidth').slice(0, -2)); let topBarFirstX = (winWidth - sheldWidth) / 2; let topBarLastX = topBarFirstX + sheldWidth; + let maxX = (width + left); + let maxY = (height + top); - //set the lowest and most-right pixel the element touches - let maxX = (draggableWidth + draggableLeft); - let maxY = (draggableHeight + draggableTop); - - // calculate the new cursor position: e = e || window.event; e.preventDefault(); @@ -538,97 +572,55 @@ export function dragElement(elmnt) { pos3 = e.clientX; //new mouse X pos4 = e.clientY; //new mouse Y - elmnt.setAttribute('data-dragged', 'true'); + elmnt.attr('data-dragged', 'true'); - //fix over/underflows: - - setTimeout(function () { - if (elmnt.offsetTop < 40) { - /* console.log('6'); */ - if (maxX > topBarFirstX && maxX < topBarLastX) { - /* console.log('maxX inside topBar!'); */ - elmnt.style.top = "42px"; - } - if (elmnt.offsetLeft < topBarLastX && elmnt.offsetLeft > topBarFirstX) { - /* console.log('offsetLeft inside TopBar!'); */ - elmnt.style.top = "42px"; - } + if (elmnt.offset().top < 40) { + if (maxX > topBarFirstX && maxX < topBarLastX) { + elmnt.css('top', '42px'); } - - if (elmnt.offsetTop - pos2 <= 0) { - /* console.log('1'); */ - //prevent going out of window top + 42px barrier for TopBar (can hide grabber) - elmnt.style.top = "0px"; + if (elmnt.offset().left < topBarLastX && elmnt.offset().left > topBarFirstX) { + elmnt.css('top', '42px'); } - - if (elmnt.offsetLeft - pos1 <= 0) { - /* console.log('2'); */ - //prevent moving out of window left - elmnt.style.left = "0px"; + } + if (elmnt.offset().top - pos2 <= 0) { + elmnt.css('top', '0px'); + } + if (elmnt.offset().left - pos1 <= 0) { + elmnt.css('left', '0px'); + } + if (maxX >= winWidth) { + elmnt.css('left', elmnt.offset().left - 10 + "px"); + } + if (maxY >= winHeight) { + elmnt.css('top', elmnt.offset().top - 10 + "px"); + if (elmnt.offset().top - pos2 <= 40) { + elmnt.css('top', '20px'); } + } + elmnt.css('left', (elmnt.offset().left - pos1) + "px"); + elmnt.css("top", (elmnt.offset().top - pos2) + "px"); + elmnt.css('margin', 'unset'); - if (maxX >= winWidth) { - /* console.log('3'); */ - //bounce off right - elmnt.style.left = elmnt.offsetLeft - 10 + "px"; - } - - if (maxY >= winHeight) { - /* console.log('4'); */ - //bounce off bottom - elmnt.style.top = elmnt.offsetTop - 10 + "px"; - if (elmnt.offsetTop - pos2 <= 40) { - /* console.log('5'); */ - //prevent going out of window top + 42px barrier for TopBar (can hide grabber) - /* console.log('caught Y bounce to <40Y top'); */ - elmnt.style.top = "20px"; - } - } - // if no problems, set element's new position - /* console.log('7'); */ - - elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; - elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; - $(elmnt).css("bottom", "unset"); - $(elmnt).css("right", "unset"); - $(elmnt).css("margin", "unset"); - - /* console.log(` - offsetLeft: ${elmnt.offsetLeft}, offsetTop: ${elmnt.offsetTop} - winWidth: ${winWidth}, winHeight: ${winHeight} - sheldWidth: ${sheldWidth} - X: ${elmnt.style.left} - Y: ${elmnt.style.top} - MaxX: ${maxX}, MaxY: ${maxY} - Topbar 1st X: ${((winWidth - sheldWidth) / 2)} - TopBar lastX: ${((winWidth - sheldWidth) / 2) + sheldWidth} - `); */ - - - - }, 50) - - /* console.log("left/top: " + (elmnt.offsetLeft - pos1) + "/" + (elmnt.offsetTop - pos2) + - ", win: " + winWidth + "/" + winHeight + - ", max X / Y: " + maxX + " / " + maxY); */ - + /* + console.log(` + winWidth: ${winWidth}, winHeight: ${winHeight} + sheldWidth: ${sheldWidth} + X: ${$(elmnt).css('left')} + Y: ${$(elmnt).css('top')} + MaxX: ${maxX}, MaxY: ${maxY} + Topbar 1st X: ${((winWidth - sheldWidth) / 2)} + TopBar lastX: ${((winWidth - sheldWidth) / 2) + sheldWidth} + `); + */ } function closeDragElement() { - // stop moving when mouse button is released: - document.onmouseup = null; - document.onmousemove = null; - //revert scrolling to normal after drag to allow recovery of vastly misplaced elements - $("body").css("overflow", "auto"); - saveMovingUIState(); - } + $(document).off('mouseup', closeDragElement); + $(document).off('mousemove', elementDrag); + $("body").css("overflow", ""); + // Clear the "data-dragged" attribute + elmnt.attr('data-dragged', 'false'); - function saveMovingUIState() { - power_user.movingUIState[elmntName].top = draggableTop; - power_user.movingUIState[elmntName].left = draggableLeft; - power_user.movingUIState[elmntName].width = draggableWidth; - power_user.movingUIState[elmntName].height = draggableHeight; - saveSettingsDebounced(); } } @@ -637,7 +629,22 @@ export function dragElement(elmnt) { $("document").ready(function () { // initial status check - setTimeout(RA_checkOnlineStatus, 100); + setTimeout(() => { + + dragElement($("#sheld")); + dragElement($("#left-nav-panel")); + dragElement($("#right-nav-panel")); + dragElement($("#WorldInfo")); + dragElement($("#floatingPrompt")); + + RA_checkOnlineStatus + } + , 100); + + + + + //$('div').on('resize', saveMovingUIState()); // read the state of AutoConnect and AutoLoadChat. $(AutoConnectCheckbox).prop("checked", LoadLocalBool("AutoConnectEnabled")); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 9fa74b09d..6d0c00778 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -15,7 +15,7 @@ import { name1, name2, } from "../script.js"; -import { favsToHotswap } from "./RossAscends-mods.js"; +import { favsToHotswap, isMobile } from "./RossAscends-mods.js"; import { groups, selected_group, @@ -25,6 +25,7 @@ import { registerSlashCommand } from "./slash-commands.js"; export { loadPowerUserSettings, + loadMovingUIState, collapseNewlines, playMessageSound, sortGroupMembers, @@ -289,9 +290,15 @@ function toggleWaifu() { } function switchWaifuMode() { - //console.log(`switching waifu to ${power_user.waifuMode}`); $("body").toggleClass("waifuMode", power_user.waifuMode); $("#waifuMode").prop("checked", power_user.waifuMode); + if (isMobile() && !$('body').hasClass('waifuMode')) { + console.debug('saw mobile regular mode, removing ZoomedAvatars'); + if ($('.zoomed_avatar[forChar]').length) { + $('.zoomed_avatar[forChar]').remove(); + } + return; + } scrollChatToBottom(); } @@ -633,15 +640,24 @@ function loadPowerUserSettings(settings, data) { switchWaifuMode(); loadMovingUIState(); - console.log(power_user) + //console.log(power_user) } function loadMovingUIState() { if (power_user.movingUIState) { for (var elmntName of Object.keys(power_user.movingUIState)) { var elmntState = power_user.movingUIState[elmntName]; - var elmnt = $('#' + elmntName); - elmnt.css(elmntState); + try { + var elmnt = $('#' + $.escapeSelector(elmntName)); + if (elmnt.length) { + console.debug(`loading state for ${elmntName}`) + elmnt.css(elmntState); + } else { + console.debug(`skipping ${elmntName} because it doesn't exist in the DOM`) + } + } catch (err) { + console.debug(`error occurred while processing ${elmntName}: ${err}`) + } } } } @@ -926,13 +942,16 @@ function resetMovablePanels() { document.getElementById("expression-holder").style.margin = ''; } - document.getElementById("avatar_zoom_popup").style.top = ''; - document.getElementById("avatar_zoom_popup").style.left = ''; - document.getElementById("avatar_zoom_popup").style.right = ''; - document.getElementById("avatar_zoom_popup").style.bottom = ''; - document.getElementById("avatar_zoom_popup").style.height = ''; - document.getElementById("avatar_zoom_popup").style.width = ''; - document.getElementById("avatar_zoom_popup").style.margin = ''; + 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', ''); + } + document.getElementById("WorldInfo").style.top = ''; document.getElementById("WorldInfo").style.left = ''; diff --git a/public/style.css b/public/style.css index 4e17cbd6e..8c42cc7ed 100644 --- a/public/style.css +++ b/public/style.css @@ -4288,7 +4288,7 @@ body.movingUI .drag-grabber { body.movingUI #sheld, body.movingUI .drawer-content, body.movingUI #expression-holder, -body.movingUI #avatar_zoom_popup, +body.movingUI .zoomed_avatar, body.movingUI #floatingPrompt { resize: both; } @@ -4315,7 +4315,7 @@ body.noShadows * { color: var(--SmartThemeBodyColor); } -#avatar_zoom_popup { +.zoomed_avatar { min-width: 100px; min-height: 100px; max-height: 90vh; @@ -4331,7 +4331,7 @@ body.noShadows * { aspect-ratio: 2 / 3; } -body.waifuMode #avatar_zoom_popup { +body.waifuMode .zoomed_avatar { min-width: 100px; min-height: 100px; max-height: 90vh; @@ -4350,7 +4350,7 @@ body.waifuMode #avatar_zoom_popup { aspect-ratio: 2 / 3; } -#zoomed_avatar { +.zoomed_avatar img { border: 1px solid var(--white50a); border-radius: 20px; height: 100%; @@ -4424,7 +4424,7 @@ body.waifuMode #avatar_zoom_popup { min-width: 100% !important; } - #avatar_zoom_popup { + .zoomed_avatar { min-width: 100px; min-height: 100px; max-height: 90vh; @@ -4665,13 +4665,14 @@ body.waifuMode #avatar_zoom_popup { object-fit: cover; } - body:not(.waifuMode) #avatar_zoom_popup { - z-index: 999; + body:not(.waifuMode) .zoomed_avatar { + display: none; + /* z-index: 999; width: fit-content; - max-height: calc(60svh - 60px); + max-height: calc(60svh - 60px); */ } - body.waifuMode #avatar_zoom_popup { + body.waifuMode .zoomed_avatar { width: fit-content; max-height: calc(60svh - 60px); max-width: 90svw; @@ -4779,4 +4780,4 @@ body.waifuMode #avatar_zoom_popup { #horde_model { height: unset; } -} +} \ No newline at end of file