From ae037c7795e0370417588a3fc04c9dcdf6ee75bb Mon Sep 17 00:00:00 2001 From: Speng Reb Date: Thu, 21 May 2026 13:46:51 +0200 Subject: [PATCH] Emote selector can be resized --- www/css/cytube.css | 37 ++++++++++++++++-- www/js/ui.js | 96 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 4 deletions(-) diff --git a/www/css/cytube.css b/www/css/cytube.css index 630e25f1..e6a7ae7a 100644 --- a/www/css/cytube.css +++ b/www/css/cytube.css @@ -625,11 +625,18 @@ table td { position: fixed; z-index: 1060; width: 340px; + height: 360px; + min-width: 260px; + min-height: 220px; + max-width: 90vw; + max-height: 85vh; background: #272b30; border: 1px solid #555; border-radius: 4px; box-shadow: 0 4px 14px rgba(0,0,0,.6); padding: 6px; + --emote-browser-item-size: 52px; + --emote-browser-image-size: 48px; } #emote-browser-search { width: 100%; @@ -643,12 +650,12 @@ table td { display: flex; flex-wrap: wrap; gap: 3px; - max-height: 320px; + height: calc(100% - 38px); overflow-y: auto; } .emote-browser-item { - width: 52px; - height: 52px; + width: var(--emote-browser-item-size); + height: var(--emote-browser-item-size); display: flex; align-items: center; justify-content: center; @@ -657,7 +664,29 @@ table td { flex-shrink: 0; } .emote-browser-item:hover { background: #3a3f44; } -.emote-browser-item img { max-width: 48px; max-height: 48px; object-fit: contain; } +.emote-browser-item img { max-width: var(--emote-browser-image-size); max-height: var(--emote-browser-image-size); object-fit: contain; } + +.emote-browser-resize-handle { + position: absolute; + width: 14px; + height: 14px; + border-radius: 50%; + background: linear-gradient(145deg, rgba(215, 215, 215, 0.65), rgba(150, 150, 150, 0.55)); + border: 1px solid rgba(18, 20, 22, 0.55); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35); + z-index: 2; + opacity: 0.85; + transition: transform 120ms ease, opacity 120ms ease, box-shadow 120ms ease; +} +.emote-browser-resize-handle:hover { + opacity: 1; + transform: scale(1.08); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45); +} +.emote-browser-resize-handle.ne { top: -7px; right: -7px; cursor: nesw-resize; } +.emote-browser-resize-handle.nw { top: -7px; left: -7px; cursor: nwse-resize; } +.emote-browser-resize-handle.se { bottom: -7px; right: -7px; cursor: nwse-resize; } +.emote-browser-resize-handle.sw { bottom: -7px; left: -7px; cursor: nesw-resize; } #leftcontrols .btn { margin-right: 5px; diff --git a/www/js/ui.js b/www/js/ui.js index 744d9c17..42fb9bec 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -962,9 +962,28 @@ $('body').append( '
' + '' + '
' + + '
' + + '
' + + '
' + + '
' + '
' ); +function updateEmoteBrowserScale() { + var panel = document.getElementById('emote-browser'); + if (!panel) return; + + var panelWidth = panel.clientWidth; + var panelHeight = panel.clientHeight; + var itemByWidth = Math.floor((panelWidth - 48) / 6); + var itemByHeight = Math.floor((panelHeight - 90) / 4); + var itemSize = Math.max(40, Math.min(88, itemByWidth, itemByHeight)); + var imageSize = Math.max(36, itemSize - 4); + + panel.style.setProperty('--emote-browser-item-size', itemSize + 'px'); + panel.style.setProperty('--emote-browser-image-size', imageSize + 'px'); +} + function emoteBrowserMatches() { if (!CHANNEL.emotes) return []; var f = EMOTE_BROWSER_FILTER.toLowerCase(); @@ -1016,6 +1035,74 @@ function emoteBrowserPosition() { panel.css({ top: top, left: left }); } +function clampEmoteBrowserToViewport() { + var panel = document.getElementById('emote-browser'); + if (!panel) return; + + var rect = panel.getBoundingClientRect(); + var maxLeft = window.innerWidth - rect.width - 8; + var maxTop = window.innerHeight - rect.height - 8; + var left = Math.min(Math.max(rect.left, 8), Math.max(8, maxLeft)); + var top = Math.min(Math.max(rect.top, 8), Math.max(8, maxTop)); + panel.style.left = left + 'px'; + panel.style.top = top + 'px'; +} + +$(document).on('mousedown', '#emote-browser .emote-browser-resize-handle', function (ev) { + ev.preventDefault(); + ev.stopPropagation(); + + var panel = document.getElementById('emote-browser'); + var dir = ev.target.getAttribute('data-dir'); + if (!panel || !dir) return; + + var startX = ev.clientX; + var startY = ev.clientY; + var startRect = panel.getBoundingClientRect(); + var minW = 260, minH = 220; + var maxW = Math.floor(window.innerWidth * 0.9); + var maxH = Math.floor(window.innerHeight * 0.85); + + function onMove(moveEv) { + var dx = moveEv.clientX - startX; + var dy = moveEv.clientY - startY; + var left = startRect.left; + var top = startRect.top; + var width = startRect.width; + var height = startRect.height; + + if (dir.indexOf('e') !== -1) { + width = Math.max(minW, Math.min(maxW, startRect.width + dx)); + } + if (dir.indexOf('s') !== -1) { + height = Math.max(minH, Math.min(maxH, startRect.height + dy)); + } + if (dir.indexOf('w') !== -1) { + width = Math.max(minW, Math.min(maxW, startRect.width - dx)); + left = startRect.right - width; + } + if (dir.indexOf('n') !== -1) { + height = Math.max(minH, Math.min(maxH, startRect.height - dy)); + top = startRect.bottom - height; + } + + panel.style.left = Math.max(8, left) + 'px'; + panel.style.top = Math.max(8, top) + 'px'; + panel.style.width = width + 'px'; + panel.style.height = height + 'px'; + updateEmoteBrowserScale(); + clampEmoteBrowserToViewport(); + } + + function onUp() { + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + } + + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); +}); + $("#emotelistbtn").on('click', function () { var panel = $("#emote-browser"); if (panel.is(':visible')) { panel.hide(); return; } @@ -1023,7 +1110,9 @@ $("#emotelistbtn").on('click', function () { $("#emote-browser-search").val(''); emoteBrowserReset(); panel.show(); + updateEmoteBrowserScale(); emoteBrowserPosition(); + clampEmoteBrowserToViewport(); document.getElementById('emote-browser-search').focus(); }); @@ -1042,6 +1131,13 @@ document.getElementById('emote-browser-grid').addEventListener('scroll', functio emoteBrowserRenderMore(); }); +$(window).on('resize', function () { + updateEmoteBrowserScale(); + if ($("#emote-browser").is(':visible')) { + clampEmoteBrowserToViewport(); + } +}); + $("#fullscreenbtn").on('click', function () { var elem = document.querySelector("#videowrap .embed-responsive"); // this shit is why frontend web development sucks