// ==UserScript==
// @name FFZ Panel Resize Center _||_ Styles ffz-menu-tree_active_color 1.2.32 ||
// @namespace http://tampermonkey.net/
// @version 1.2.32
// @description combined into one extension ,emote picker,resize ffz panel.
// @author gullampis810
// @match https://www.twitch.tv/*
// @license MIT
// @icon https://png.pngtree.com/png-vector/20220703/ourmid/pngtree-send-dark-mode-glyph-icon-png-image_5561369.png
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
// FFZ_main_Panel_Resize.js v1.2.30 //
(function() {
'use strict';
// --- Часть 1: Изменение размеров панели эмодзи FFZ ---
const observerEmoji = new MutationObserver(() => {
const buttonContainer = document.querySelector('.tw-absolute.tw-border-radius-medium.tw-bottom-0.tw-c-background-overlay.tw-c-text-overlay.tw-mg-b-1');
if (buttonContainer) {
buttonContainer.style.height = '34px';
buttonContainer.style.minHeight = '34px';
buttonContainer.style.maxHeight = '34px';
console.log('[FFZ Enhancements] Высота контейнера кнопки установлена на 34px');
}
});
observerEmoji.observe(document.body, { childList: true, subtree: true });
console.log('[FFZ Enhancements] Контейнер .emote-picker изменен: шире, выше, сдвинут влево.');
// --- Часть 2: Перетаскивание и изменение размеров FFZ-диалога ---
function isInputElement(target) {
return target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.tagName === 'SELECT' ||
target.closest('input, textarea, select');
}
function initDraggingAndResizing(dialog) {
if (!dialog) {
console.log('[FFZ Enhancements] Dialog not found');
return;
}
const header = dialog.querySelector('header');
if (!header) {
console.log('[FFZ Enhancements] Header not found');
return;
}
if (dialog.dataset.draggingInitialized) {
console.log('[FFZ Enhancements] Dragging already initialized, skipping');
return;
}
dialog.dataset.draggingInitialized = 'true';
console.log('[FFZ Enhancements] Initializing for dialog');
dialog.style.position = 'absolute';
dialog.style.minWidth = '200px';
dialog.style.minHeight = '200px';
let isDragging = false;
let startX, startY;
let isResizing = false;
let resizeStartX, resizeStartY;
header.addEventListener('mousedown', (e) => {
if (e.target.closest('button') || isInputElement(e.target)) {
console.log('[FFZ Enhancements] Ignoring button or input click');
return;
}
isDragging = true;
startX = e.clientX - (parseFloat(dialog.style.left) || dialog.offsetLeft);
startY = e.clientY - (parseFloat(dialog.style.top) || dialog.offsetTop);
dialog.style.zIndex = Math.max(parseInt(dialog.style.zIndex) || 9000, 9000) + 1;
console.log('[FFZ Enhancements] Drag started at', e.clientX, e.clientY);
e.preventDefault();
e.stopPropagation();
}, { capture: true, passive: false });
document.addEventListener('mousemove', (e) => {
if (isDragging) {
requestAnimationFrame(() => {
const newLeft = e.clientX - startX;
const newTop = e.clientY - startY;
dialog.style.left = `${newLeft}px`;
dialog.style.top = `${newTop}px`;
console.log('[FFZ Enhancements] Moved to', newLeft, newTop);
});
}
}, { capture: true, passive: true });
document.addEventListener('mouseup', () => {
if (isDragging) {
console.log('[FFZ Enhancements] Drag ended');
isDragging = false;
}
}, { capture: true, passive: true });
header.addEventListener('touchstart', (e) => {
if (e.target.closest('button') || isInputElement(e.target)) {
console.log('[FFZ Enhancements] Ignoring button or input touch');
return;
}
isDragging = true;
const touch = e.touches[0];
startX = touch.clientX - (parseFloat(dialog.style.left) || dialog.offsetLeft);
startY = touch.clientY - (parseFloat(dialog.style.top) || dialog.offsetTop);
dialog.style.zIndex = Math.max(parseInt(dialog.style.zIndex) || 9000, 9000) + 1;
console.log('[FFZ Enhancements] Touch drag started at', touch.clientX, touch.clientY);
e.preventDefault();
e.stopPropagation();
}, { capture: true, passive: false });
document.addEventListener('touchmove', (e) => {
if (isDragging) {
const touch = e.touches[0];
requestAnimationFrame(() => {
const newLeft = touch.clientX - startX;
const newTop = touch.clientY - startY;
dialog.style.left = `${newLeft}px`;
dialog.style.top = `${newTop}px`;
console.log('[FFZ Enhancements] Touch moved to', newLeft, newTop);
});
}
}, { capture: true, passive: true });
document.addEventListener('touchend', () => {
if (isDragging) {
console.log('[FFZ Enhancements] Touch drag ended');
isDragging = false;
}
}, { capture: true, passive: true });
const resizeHandle = document.createElement('div');
resizeHandle.className = '.ffz-panel-7btvfz-5e8jhe5-resize-handle';
resizeHandle.style.position = 'absolute';
resizeHandle.style.bottom = '4px';
resizeHandle.style.right = '4px';
resizeHandle.style.width = '15px';
resizeHandle.style.height = '15px';
resizeHandle.style.background = '#34767c';
resizeHandle.style.cursor = 'se-resize';
resizeHandle.style.zIndex = '10001';
resizeHandle.style.border = '1px solid #ffffff';
resizeHandle.style.borderRadius = '8px';
dialog.appendChild(resizeHandle);
resizeHandle.addEventListener('mousedown', (e) => {
if (isInputElement(e.target)) {
console.log('[FFZ Enhancements] Ignoring input click on resize handle');
return;
}
isResizing = true;
resizeStartX = e.clientX;
resizeStartY = e.clientY;
console.log('[FFZ Enhancements] Resize started at', e.clientX, e.clientY);
e.preventDefault();
e.stopPropagation();
}, { capture: true, passive: false });
document.addEventListener('mousemove', (e) => {
if (isResizing) {
requestAnimationFrame(() => {
const newWidth = (parseFloat(dialog.style.width) || dialog.offsetWidth) + (e.clientX - resizeStartX);
const newHeight = (parseFloat(dialog.style.height) || dialog.offsetHeight) + (e.clientY - resizeStartY);
if (newWidth >= 200) {
dialog.style.width = `${newWidth}px`;
resizeStartX = e.clientX;
}
if (newHeight >= 200) {
dialog.style.height = `${newHeight}px`;
resizeStartY = e.clientY;
}
console.log('[FFZ Enhancements] Resized to', newWidth, newHeight);
});
}
}, { capture: true, passive: true });
document.addEventListener('mouseup', () => {
if (isResizing) {
console.log('[FFZ Enhancements] Resize ended');
isResizing = false;
}
}, { capture: true, passive: true });
resizeHandle.addEventListener('touchstart', (e) => {
if (isInputElement(e.target)) {
console.log('[FFZ Enhancements] Ignoring input touch on resize handle');
return;
}
isResizing = true;
const touch = e.touches[0];
resizeStartX = touch.clientX;
resizeStartY = touch.clientY;
console.log('[FFZ Enhancements] Touch resize started at', touch.clientX, touch.clientY);
e.preventDefault();
e.stopPropagation();
}, { capture: true, passive: false });
document.addEventListener('touchmove', (e) => {
if (isResizing) {
const touch = e.touches[0];
requestAnimationFrame(() => {
const newWidth = (parseFloat(dialog.style.width) || dialog.offsetWidth) + (touch.clientX - resizeStartX);
const newHeight = (parseFloat(dialog.style.height) || dialog.offsetHeight) + (touch.clientY - resizeStartY);
if (newWidth >= 200) {
dialog.style.width = `${newWidth}px`;
resizeStartX = touch.clientX;
}
if (newHeight >= 200) {
dialog.style.height = `${newHeight}px`;
resizeStartY = touch.clientY;
}
console.log('[FFZ Enhancements] Touch resized to', newWidth, newHeight);
});
}
}, { capture: true, passive: true });
document.addEventListener('touchend', () => {
if (isResizing) {
console.log('[FFZ Enhancements] Touch resize ended');
isResizing = false;
}
}, { capture: true, passive: true });
const resetButton = document.createElement('button');
resetButton.textContent = 'Reset Position';
resetButton.style.position = 'absolute';
resetButton.style.top = '9px';
resetButton.style.right = '180px';
resetButton.style.zIndex = '1';
resetButton.style.padding = '5px';
resetButton.style.background = '#34767c';
resetButton.style.borderRadius = '12px';
resetButton.style.border = '1px solid #ffffff';
resetButton.addEventListener('click', () => {
dialog.style.left = '25%';
dialog.style.top = '25%';
dialog.style.width = '';
dialog.style.height = '';
console.log('[FFZ Enhancements] Position and size reset to 25%, 25%');
});
dialog.appendChild(resetButton);
console.log('[FFZ Enhancements] Dragging and resizing initialized successfully');
}
const observerDialog = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
const dialog = document.querySelector('.ffz-dialog.ffz-main-menu:not([data-dragging-initialized])');
if (dialog) {
console.log('[FFZ Enhancements] Dialog detected via observer');
initDraggingAndResizing(dialog);
}
}
}
});
observerDialog.observe(document.body, {
childList: true,
subtree: true
});
const initialDialog = document.querySelector('.ffz-dialog.ffz-main-menu:not([data-dragging-initialized])');
if (initialDialog) {
console.log('[FFZ Enhancements] Initial dialog found');
initDraggingAndResizing(initialDialog);
}
// --- Часть 3: Улучшение ffz-viewer-card ---
function setupCustomDrag(card) {
const header = card.querySelector('.ffz-viewer-card__header');
if (!header) {
console.log('[FFZ Enhancements] Header not found for dragging');
return;
}
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
header.addEventListener('mousedown', (e) => {
if (e.target.closest('.viewer-card-drag-cancel')) return;
isDragging = true;
initialX = e.clientX - currentX;
initialY = e.clientY - currentY;
card.style.transition = 'none';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
card.style.left = `${currentX}px`;
card.style.top = `${currentY}px`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
card.style.transition = '';
});
if (!card.style.left || !card.style.top) {
const rect = card.getBoundingClientRect();
currentX = rect.left;
currentY = rect.top;
card.style.left = `${currentX}px`;
card.style.top = `${currentY}px`;
}
}
function showEmoteSelectionPopup(emotes, callback) {
console.log('[FFZ Enhancements] Attempting to show emote selection popup with emotes:', emotes);
const existingPopup = document.getElementById('emote-selection-popup');
if (existingPopup) {
console.log('[FFZ Enhancements] Removing existing popup');
existingPopup.remove();
}
const popup = document.createElement('div');
popup.id = 'emote-selection-popup';
popup.innerHTML = `
<div class="close-button" style="cursor:pointer;position:absolute;top:6px;right:10px;font-size:20px;">✕</div>
<div class="emote-options"></div>
`;
document.body.appendChild(popup);
console.log('[FFZ Enhancements] Popup element created and appended to body');
popup.classList.add('ffz-emote-popup');
const optionsContainer = popup.querySelector('.emote-options');
emotes.forEach((emote, index) => {
const option = document.createElement('div');
option.className = 'emote-option';
option.style.display = 'flex';
option.style.alignItems = 'center';
option.style.justifyContent = 'space-between';
option.style.padding = '8px 0';
option.style.borderBottom = '1px solid rgba(115, 209, 204, 0.16)';
option.style.gap = '10px';
const left = document.createElement('div');
left.style.display = 'flex';
left.style.alignItems = 'center';
left.style.minWidth = '0';
const img = document.createElement('img');
img.src = emote.src || '';
img.alt = emote.alt || 'Emote';
img.style.width = '24px';
img.style.height = '24px';
img.style.marginRight = '10px';
img.style.flexShrink = '0';
img.style.userSelect = 'none';
const info = document.createElement('div');
info.className = 'emote-info';
info.style.fontSize = '14px';
info.style.whiteSpace = 'nowrap';
info.style.overflow = 'hidden';
info.style.textOverflow = 'ellipsis';
info.innerHTML = `<span>${emote.alt || 'Unnamed'} <span style="user-select:none;">(${emote.platform})</span></span>`;
left.appendChild(img);
left.appendChild(info);
const blockButton = document.createElement('button');
blockButton.className = 'block-button';
blockButton.type = 'button';
blockButton.textContent = 'Block';
blockButton.classList.add('ffz-block-button');
blockButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log('[FFZ Enhancements] Block button clicked for emote:', emote);
callback(emote);
if (emote.element) {
emote.element.style.display = 'none';
console.log('[FFZ Enhancements] Emote element hidden:', emote.alt);
const parentContainer = emote.element.closest('.ffz--inline, .chat-line__message, .chat-image');
if (parentContainer) {
const allEmotes = parentContainer.querySelectorAll(
'img.chat-line__message--emote, .ffz-emote, .seventv-emote, .bttv-emote, .twitch-emote, .chat-image'
);
const allBlocked = Array.from(allEmotes).every(e => e.style.display === 'none');
if (allBlocked) {
parentContainer.style.display = 'none';
console.log('[FFZ Enhancements] Parent container hidden as all emotes are blocked');
}
}
}
popup.remove();
});
option.appendChild(left);
option.appendChild(blockButton);
optionsContainer.appendChild(option);
});
console.log('[FFZ Enhancements] Popup populated with', emotes.length, 'emotes');
const closeButton = popup.querySelector('.close-button');
closeButton.onclick = () => {
console.log('[FFZ Enhancements] Emote selection popup closed via close button');
popup.remove();
};
const computedStyle = window.getComputedStyle(popup);
if (computedStyle.display === 'none' || computedStyle.visibility === 'hidden') {
console.warn('[FFZ Enhancements] Popup is not visible, forcing visibility');
popup.style.display = 'block';
popup.style.visibility = 'visible';
}
setTimeout(() => {
popup.classList.add('visible');
console.log('[FFZ Enhancements] Popup visibility class applied');
}, 10);
document.addEventListener('click', function closePopup(e) {
const viewerCard = document.querySelector('.ffz-viewer-card.tw-border');
if (!popup.contains(e.target) && e.target !== popup && (!viewerCard || !viewerCard.contains(e.target))) {
console.log('[FFZ Enhancements] Closing popup due to outside click');
popup.remove();
document.removeEventListener('click', closePopup);
}
}, { capture: true, once: true });
const viewerCard = document.querySelector('.ffz-viewer-card.tw-border');
if (viewerCard) {
const observer = new MutationObserver(() => {
const rect = viewerCard.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const popupWidth = 320;
const offset = 20;
const extraOffset = 30;
let left = rect.right + offset + extraOffset;
let top = rect.top;
if (left + popupWidth > viewportWidth) {
left = rect.left - popupWidth - offset;
}
if (top + popup.offsetHeight > viewportHeight) {
top = viewportHeight - popup.offsetHeight - offset;
}
if (top < 0) {
top = offset;
}
if (left < 0) {
left = offset;
if (rect.bottom + popup.offsetHeight + offset <= viewportHeight) {
left = rect.left;
top = rect.bottom + offset;
}
}
popup.style.left = `${left}px`;
popup.style.top = `${top}px`;
console.log('[FFZ Enhancements] Popup repositioned to left:', left, 'top:', top);
});
observer.observe(viewerCard, {
attributes: true,
attributeFilter: ['style', 'class']
});
popup.addEventListener('remove', () => {
observer.disconnect();
console.log('[FFZ Enhancements] MutationObserver disconnected');
});
const rect = viewerCard.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const popupWidth = 320;
const offset = 20;
const extraOffset = 30;
let left = rect.right + offset + extraOffset;
let top = rect.top;
if (left + popupWidth > viewportWidth) {
left = rect.left - popupWidth - offset;
}
if (top + popup.offsetHeight > viewportHeight) {
top = viewportHeight - popup.offsetHeight - offset;
}
if (top < 0) {
top = offset;
}
if (left < 0) {
left = offset;
if (rect.bottom + popup.offsetHeight + offset <= viewportHeight) {
left = rect.left;
top = rect.bottom + offset;
}
}
popup.style.left = `${left}px`;
popup.style.top = `${top}px`;
console.log('[FFZ Enhancements] Popup positioned at left:', left, 'top:', top);
} else {
popup.style.right = '310px';
popup.style.top = '385px';
console.warn('[FFZ Enhancements] ffz-viewer-card not found, using fallback position');
}
}
function enableUnconstrainedDragging() {
const observer = new MutationObserver((mutations, obs) => {
const viewerCard = document.querySelector('.ffz-viewer-card.tw-border');
if (viewerCard) {
setupCustomDrag(viewerCard);
obs.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
const viewerCard = document.querySelector('.ffz-viewer-card.tw-border');
if (viewerCard) {
setupCustomDrag(viewerCard);
}
}
enableUnconstrainedDragging();
window.showEmoteSelectionPopup = showEmoteSelectionPopup;
console.log('[FFZ Enhancements] Setup complete');
})();
// ffz--menu-tree_active_color.js v1.2.28 //
(function() {
'use strict';
// Создаём элемент <style> и добавляем CSS
const style = document.createElement('style');
style.textContent = `
/* Элементы верхнего уровня */
.ffz--menu-tree > li[role="presentation"] > div.tw-flex__item[role="treeitem"] {
background-color: #346056 !important;
color: #90e2da !important;
}
/* Вложенные элементы (не активные) не наследуют фон */
.ffz--menu-tree ul[role="group"] li[role="presentation"] > div.tw-flex__item[role="treeitem"]:not([aria-selected="true"]) {
background-color: transparent !important;
}
/* Стили для активных разделов (верхний уровень и вложенные) */
.ffz--menu-tree li[role="presentation"].active > div.tw-flex__item[role="treeitem"],
.ffz--menu-tree li[role="presentation"] > div.tw-flex__item[role="treeitem"][aria-selected="true"] {
background-color: #321b47 !important;
color: #68e375 !important;
opacity: 1 !important;
z-index: 1 !important;
}
.ffz-resizer {
user-select: none !important;
}
.ffz-resizing {
user-select: all !important;
}
header.tw-c-background-base.tw-full-width.tw-align-items-center.tw-flex.tw-flex-nowrap {
overflow: hidden !important;
white-space: nowrap !important;
}
.ffz-color-preview.tw-absolute.tw-top-0.tw-bottom-0.tw-right-0.tw-border-l.tw-z-default {
height: 33px !important;
}
`;
document.head.appendChild(style);
// Функция для принудительного применения стилей
function applyActiveStyles() {
const activeItems = document.querySelectorAll(
'.ffz--menu-tree li[role="presentation"].active > div.tw-flex__item[role="treeitem"], ' +
'.ffz--menu-tree li[role="presentation"] > div.tw-flex__item[role="treeitem"][aria-selected="true"]'
);
activeItems.forEach(item => {
item.style.backgroundColor = '#321b47';
item.style.color = '#68e375';
item.style.opacity = '1';
item.style.zIndex = '1';
});
}
// Выполняем сразу
applyActiveStyles();
// Периодическое обновление стилей
const styleInterval = setInterval(applyActiveStyles, 500);
// Наблюдение за изменениями в DOM
const observer = new MutationObserver(() => {
applyActiveStyles();
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'aria-selected', 'style']
});
// Остановка интервала и наблюдателя через 10 секунд
setTimeout(() => {
clearInterval(styleInterval);
observer.disconnect();
}, 10000);
})();
// resize_navsettings_Menu_ffz.js v1.4.0 //
(function() {
'use strict';
// Создаём элемент <style> для стилей
const style = document.createElement('style');
style.textContent = `
.ffz-vertical-nav {
position: relative;
transition: width 0.2s ease;
min-width: 100px !important;
max-width: 500px !important;
}
.ffz-tree-nav-7btvfz-menu-settings-resize-handle {
position: absolute;
top: 0;
right: -5px;
width: 5px;
height: 100%;
background: #34767c;
cursor: ew-resize;
z-index: 10001;
pointer-events: auto !important;
user-select: none !important;
touch-action: none !important;
}
.ffz-resizing {
user-select: none !important;
pointer-events: auto !important;
}
.ffz--menu-tree > li[role="presentation"] > div.tw-flex__item[role="treeitem"] {
background-color: #346056 !important;
color: #90e2da !important;
}
.ffz--menu-tree ul[role="group"] li[role="presentation"] > div.tw-flex__item[role="treeitem"]:not([aria-selected="true"]) {
background-color: transparent !important;
}
.ffz--menu-tree li[role="presentation"].active > div.tw-flex__item[role="treeitem"],
.ffz--menu-tree li[role="presentation"] > div.tw-flex__item[role="treeitem"][aria-selected="true"] {
background-color: #321b47 !important;
color: #68e375 !important;
opacity: 1 !important;
z-index: 1 !important;
}
`;
document.head.appendChild(style);
// Функция для сохранения ширины панели
function savePanelWidth(width) {
localStorage.setItem('ffz_panel_width', width);
console.log('[FFZ Resize] Saved width:', width);
}
// Функция для восстановления ширины панели
function restorePanelWidth(nav) {
const savedWidth = localStorage.getItem('ffz_panel_width');
if (savedWidth && nav) {
nav.style.width = savedWidth;
console.log('[FFZ Resize] Restored width:', savedWidth);
}
}
// Функция для проверки, является ли элемент полем ввода
function isInputElement(target) {
return target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.tagName === 'SELECT' ||
target.closest('input, textarea, select');
}
// Функция для инициализации ресайзера
function initResizing(nav) {
if (!nav) {
console.log('[FFZ Resize] Nav not found');
return;
}
if (nav.dataset.resizeInitialized) {
console.log('[FFZ Resize] Resizing already initialized, skipping');
return;
}
nav.dataset.resizeInitialized = 'true';
console.log('[FFZ Resize] Initializing resizing for nav');
// Создаём ручку ресайза
let resizeHandle = nav.querySelector('.ffz-tree-nav-7btvfz-menu-6je5kyfred-settings-resize-handle');
if (!resizeHandle) {
resizeHandle = document.createElement('div');
resizeHandle.className = 'ffz-tree-nav-7btvfz-menu-6je5kyfred-settings-resize-handle';
nav.appendChild(resizeHandle);
console.log('[FFZ Resize] Created new resize handle');
}
// Применяем стили к ручке
resizeHandle.style.position = 'absolute';
resizeHandle.style.top = '0';
resizeHandle.style.right = '-5px';
resizeHandle.style.width = '5px';
resizeHandle.style.height = '100%';
resizeHandle.style.background = '#34767c';
resizeHandle.style.cursor = 'ew-resize';
resizeHandle.style.zIndex = '10001';
resizeHandle.style.pointerEvents = 'auto';
resizeHandle.style.userSelect = 'none';
resizeHandle.style.touchAction = 'none';
let isResizing = false;
let startX, startWidth;
// Обработчик для мыши
resizeHandle.addEventListener('mousedown', (e) => {
if (isInputElement(e.target)) {
console.log('[FFZ Resize] Ignoring input click on resize handle');
return;
}
isResizing = true;
startX = e.clientX;
startWidth = parseInt(getComputedStyle(nav).width, 10);
nav.classList.add('ffz-resizing');
console.log('[FFZ Resize] Resize started at', e.clientX, startWidth);
e.preventDefault();
e.stopPropagation();
}, { capture: true, passive: false });
document.addEventListener('mousemove', (e) => {
if (!isResizing) return;
requestAnimationFrame(() => {
const newWidth = startWidth + (e.clientX - startX);
if (newWidth >= 100 && newWidth <= 500) {
nav.style.width = `${newWidth}px`;
savePanelWidth(`${newWidth}px`);
console.log('[FFZ Resize] Resized to', newWidth);
}
});
}, { capture: true, passive: true });
document.addEventListener('mouseup', () => {
if (isResizing) {
console.log('[FFZ Resize] Resize ended');
isResizing = false;
nav.classList.remove('ffz-resizing');
}
}, { capture: true, passive: true });
// Обработчик для сенсорных устройств
resizeHandle.addEventListener('touchstart', (e) => {
if (isInputElement(e.target)) {
console.log('[FFZ Resize] Ignoring input touch on resize handle');
return;
}
isResizing = true;
const touch = e.touches[0];
startX = touch.clientX;
startWidth = parseInt(getComputedStyle(nav).width, 10);
nav.classList.add('ffz-resizing');
console.log('[FFZ Resize] Touch resize started at', touch.clientX, startWidth);
e.preventDefault();
e.stopPropagation();
}, { capture: true, passive: false });
document.addEventListener('touchmove', (e) => {
if (!isResizing) return;
const touch = e.touches[0];
requestAnimationFrame(() => {
const newWidth = startWidth + (touch.clientX - startX);
if (newWidth >= 100 && newWidth <= 500) {
nav.style.width = `${newWidth}px`;
savePanelWidth(`${newWidth}px`);
console.log('[FFZ Resize] Touch resized to', newWidth);
}
});
}, { capture: true, passive: true });
document.addEventListener('touchend', () => {
if (isResizing) {
console.log('[FFZ Resize] Touch resize ended');
isResizing = false;
nav.classList.remove('ffz-resizing');
}
}, { capture: true, passive: true });
// Восстанавливаем ширину
restorePanelWidth(nav);
}
// Наблюдение за DOM
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length) {
const nav = document.querySelector('.ffz-vertical-nav:not([data-resize-initialized])');
if (nav) {
console.log('[FFZ Resize] Nav detected via observer');
initResizing(nav);
}
// Проверяем Shadow DOM
const shadowHosts = document.querySelectorAll('div[class*="ffz--settings"]');
shadowHosts.forEach(host => {
if (host.shadowRoot) {
const shadowNav = host.shadowRoot.querySelector('.ffz-vertical-nav:not([data-resize-initialized])');
if (shadowNav) {
console.log('[FFZ Resize] Nav detected in Shadow DOM');
initResizing(shadowNav);
}
}
});
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'style']
});
// Инициализация при загрузке
const initialNav = document.querySelector('.ffz-vertical-nav:not([data-resize-initialized])');
if (initialNav) {
console.log('[FFZ Resize] Initial nav found');
initResizing(initialNav);
}
// Периодическая проверка
setInterval(() => {
const nav = document.querySelector('.ffz-vertical-nav:not([data-resize-initialized])');
if (nav) {
console.log('[FFZ Resize] Periodic check: nav found, initializing');
initResizing(nav);
}
// Восстанавливаем ширину для всех панелей, даже если они уже инициализированы
const allNavs = document.querySelectorAll('.ffz-vertical-nav');
allNavs.forEach(nav => restorePanelWidth(nav));
}, 500);
console.log('[FFZ Resize] Setup complete');
})();
// emote_picker_combo_resize //
(function() {
'use strict';
// Добавляем стили для изменения размеров контейнера эмодзи
GM_addStyle(`
.emote-picker__controls-container.tw-relative {
bottom: 3px;
}
.emote-picker {
width: 107rem !important; /* Увеличенная ширина */
height: 100rem !important; /* Увеличенная высота */
left: 24px; /* Сдвиг влево */
position: relative;
}
.ffz--emote-picker {
position: relative !important;
height: 785px !important;
width: 1097px !important;
left: -243px !important;
}
.ffz--emote-picker.ffz--emote-picker__tall .emote-picker__nav-content-overflow, .ffz--emote-picker.ffz--emote-picker__tall .emote-picker__tab-content {
height: unset !important;
max-height: 73rem !important;
}
.tw-absolute.ffz-attached.ffz-attached--right.ffz-attached--up {
width: 857px !important;
right: 368px !important;
bottom: 533px !important;
}
/* fix ballon when clicked ffz emote picke in chat input */
.ffz-balloon.ffz-balloon--auto.tw-inline-block.tw-border-radius-large.tw-c-background-base.tw-c-text-inherit.tw-elevation-2.ffz--emote-picker.ffz--emote-picker__tall {
top: 290px !important;
}
.ffz-attached--up {
bottom: 510% !important;
}
.tw-border-b.tw-border-l.tw-border-r.tw-border-t.tw-border-radius-medium.tw-c-background-base.tw-elevation-1 {
width: 63px;
height: 216px;
}
.tw-absolute {
position: absolute !important;
height: 570px !important;
}
.tw-border-b.tw-border-l.tw-border-r.tw-border-t.tw-border-radius-medium.tw-c-background-base.tw-elevation-1 {
width: 60px !important;
height: 200px !important;
bottom: 6px !important;
position: absolute !important;
right: 5px !important;
}
// emoji standard color choice mini panel //
`);
console.log("[FFZ Emote Panel] Контейнер .emote-picker изменен: шире, выше, сдвинут влево.");
})();