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